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     if ( label == comment ) comment = QString::null;
01738 
01739     QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
01740 
01741     QString href = fileName.isEmpty() ?
01742       "part://" + QString::number( partNum + 1 ) :
01743       "file:" + KURL::encode_string( fileName ) ;
01744 
01745     QString iconName;
01746     if( inlineImage )
01747       iconName = href;
01748     else {
01749       iconName = msgPart->iconName();
01750       if( iconName.right( 14 ) == "mime_empty.png" ) {
01751         msgPart->magicSetType();
01752         iconName = msgPart->iconName();
01753       }
01754     }
01755 
01756     QCString contentId = msgPart->contentId();
01757     if ( !contentId.isEmpty() ) {
01758       htmlWriter()->embedPart( contentId, href );
01759     }
01760 
01761     if( inlineImage )
01762       // show the filename of the image below the embedded image
01763       htmlWriter()->queue( "<div><a href=\"" + href + "\">"
01764                            "<img src=\"" + iconName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
01765                            "</div>"
01766                            "<div><a href=\"" + href + "\">" + label + "</a>"
01767                            "</div>"
01768                            "<div>" + comment + "</div><br>" );
01769     else
01770       // show the filename next to the image
01771       htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
01772                            iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
01773                            "</a></div>"
01774                            "<div>" + comment + "</div><br>" );
01775   }
01776 
01777 #define SIG_FRAME_COL_UNDEF  99
01778 #define SIG_FRAME_COL_RED    -1
01779 #define SIG_FRAME_COL_YELLOW  0
01780 #define SIG_FRAME_COL_GREEN   1
01781 QString ObjectTreeParser::sigStatusToString( CryptPlugWrapper* cryptPlug,
01782                                         int status_code,
01783                                         CryptPlugWrapper::SigStatusFlags statusFlags,
01784                                         int& frameColor,
01785                                         bool& showKeyInfos )
01786 {
01787     // note: At the moment frameColor and showKeyInfos are
01788     //       used for CMS only but not for PGP signatures
01789     // pending(khz): Implement usage of these for PGP sigs as well.
01790     showKeyInfos = true;
01791     QString result;
01792     if( cryptPlug ) {
01793         if( cryptPlug->protocol().lower() == "openpgp" ) {
01794             // process enum according to it's definition to be read in
01795             // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
01796             switch( status_code ) {
01797             case 0: // GPGME_SIG_STAT_NONE
01798                 result = i18n("Error: Signature not verified");
01799                 break;
01800             case 1: // GPGME_SIG_STAT_GOOD
01801                 result = i18n("Good signature");
01802                 break;
01803             case 2: // GPGME_SIG_STAT_BAD
01804                 result = i18n("<b>Bad</b> signature");
01805                 break;
01806             case 3: // GPGME_SIG_STAT_NOKEY
01807                 result = i18n("No public key to verify the signature");
01808                 break;
01809             case 4: // GPGME_SIG_STAT_NOSIG
01810                 result = i18n("No signature found");
01811                 break;
01812             case 5: // GPGME_SIG_STAT_ERROR
01813                 result = i18n("Error verifying the signature");
01814                 break;
01815             case 6: // GPGME_SIG_STAT_DIFF
01816                 result = i18n("Different results for signatures");
01817                 break;
01818             /* PENDING(khz) Verify exact meaning of the following values:
01819             case 7: // GPGME_SIG_STAT_GOOD_EXP
01820                 return i18n("Signature certificate is expired");
01821             break;
01822             case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
01823                 return i18n("One of the certificate's keys is expired");
01824             break;
01825             */
01826             default:
01827                 result = "";   // do *not* return a default text here !
01828                 break;
01829             }
01830         }
01831         else if ( cryptPlug->protocol().lower() == "smime" ) {
01832             // process status bits according to SigStatus_...
01833             // definitions in kdenetwork/libkdenetwork/cryptplug.h
01834 
01835             if( CryptPlugWrapper::SigStatus_UNKNOWN == statusFlags ) {
01836                 result = i18n("No status information available.");
01837                 frameColor = SIG_FRAME_COL_YELLOW;
01838                 showKeyInfos = false;
01839                 return result;
01840             }
01841 
01842             if( CryptPlugWrapper::SigStatus_VALID & statusFlags ) {
01843                 result = i18n("Good signature.");
01844                 // Note:
01845                 // Here we are work differently than KMail did before!
01846                 //
01847                 // The GOOD case ( == sig matching and the complete
01848                 // certificate chain was verified and is valid today )
01849                 // by definition does *not* show any key
01850                 // information but just states that things are OK.
01851                 //           (khz, according to LinuxTag 2002 meeting)
01852                 frameColor = SIG_FRAME_COL_GREEN;
01853                 showKeyInfos = false;
01854                 return result;
01855             }
01856 
01857             // we are still there?  OK, let's test the different cases:
01858 
01859             // we assume green, test for yellow or red (in this order!)
01860             frameColor = SIG_FRAME_COL_GREEN;
01861             QString result2;
01862             if( CryptPlugWrapper::SigStatus_KEY_EXPIRED & statusFlags ){
01863                 // still is green!
01864                 result2 += i18n("One key has expired.");
01865             }
01866             if( CryptPlugWrapper::SigStatus_SIG_EXPIRED & statusFlags ){
01867                 // and still is green!
01868                 result2 += i18n("The signature has expired.");
01869             }
01870 
01871             // test for yellow:
01872             if( CryptPlugWrapper::SigStatus_KEY_MISSING & statusFlags ) {
01873                 result2 += i18n("Unable to verify: key missing.");
01874                 // if the signature certificate is missing
01875                 // we cannot show infos on it
01876                 showKeyInfos = false;
01877                 frameColor = SIG_FRAME_COL_YELLOW;
01878             }
01879             if( CryptPlugWrapper::SigStatus_CRL_MISSING & statusFlags ){
01880                 result2 += i18n("CRL not available.");
01881                 frameColor = SIG_FRAME_COL_YELLOW;
01882             }
01883             if( CryptPlugWrapper::SigStatus_CRL_TOO_OLD & statusFlags ){
01884                 result2 += i18n("Available CRL is too old.");
01885                 frameColor = SIG_FRAME_COL_YELLOW;
01886             }
01887             if( CryptPlugWrapper::SigStatus_BAD_POLICY & statusFlags ){
01888                 result2 += i18n("A policy was not met.");
01889                 frameColor = SIG_FRAME_COL_YELLOW;
01890             }
01891             if( CryptPlugWrapper::SigStatus_SYS_ERROR & statusFlags ){
01892                 result2 += i18n("A system error occurred.");
01893                 // if a system error occurred
01894                 // we cannot trust any information
01895                 // that was given back by the plug-in
01896                 showKeyInfos = false;
01897                 frameColor = SIG_FRAME_COL_YELLOW;
01898             }
01899             if( CryptPlugWrapper::SigStatus_NUMERICAL_CODE & statusFlags ) {
01900                 result2 += i18n("Internal system error #%1 occurred.")
01901                         .arg( statusFlags - CryptPlugWrapper::SigStatus_NUMERICAL_CODE );
01902                 // if an unsupported internal error occurred
01903                 // we cannot trust any information
01904                 // that was given back by the plug-in
01905                 showKeyInfos = false;
01906                 frameColor = SIG_FRAME_COL_YELLOW;
01907             }
01908 
01909             // test for red:
01910             if( CryptPlugWrapper::SigStatus_KEY_REVOKED & statusFlags ){
01911                 // this is red!
01912                 result2 += i18n("One key has been revoked.");
01913                 frameColor = SIG_FRAME_COL_RED;
01914             }
01915             if( CryptPlugWrapper::SigStatus_RED & statusFlags ) {
01916                 if( result2.isEmpty() )
01917                     // Note:
01918                     // Here we are work differently than KMail did before!
01919                     //
01920                     // The BAD case ( == sig *not* matching )
01921                     // by definition does *not* show any key
01922                     // information but just states that things are BAD.
01923                     //
01924                     // The reason for this: In this case ALL information
01925                     // might be falsificated, we can NOT trust the data
01926                     // in the body NOT the signature - so we don't show
01927                     // any key/signature information at all!
01928                     //         (khz, according to LinuxTag 2002 meeting)
01929                     showKeyInfos = false;
01930                 frameColor = SIG_FRAME_COL_RED;
01931             }
01932             else
01933                 result = "";
01934 
01935             if( SIG_FRAME_COL_GREEN == frameColor ) {
01936                 result = i18n("Good signature.");
01937             } else if( SIG_FRAME_COL_RED == frameColor ) {
01938                 result = i18n("<b>Bad</b> signature.");
01939             } else
01940                 result = "";
01941 
01942             if( !result2.isEmpty() ) {
01943                 if( !result.isEmpty() )
01944                     result.append("<br />");
01945                 result.append( result2 );
01946             }
01947         }
01948         /*
01949         // add i18n support for 3rd party plug-ins here:
01950         else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
01951 
01952         }
01953         */
01954     }
01955     return result;
01956 }
01957 
01958 
01959 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
01960                                               CryptPlugWrapper * cryptPlug,
01961                                               const QString & fromAddress,
01962                                               const QString & filename )
01963 {
01964     bool isSMIME = cryptPlug && cryptPlug->protocol().lower() == "smime";
01965     QString signer = block.signer;
01966 
01967     QString htmlStr;
01968     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01969     QString cellPadding("cellpadding=\"1\"");
01970 
01971     if( block.isEncapsulatedRfc822Message )
01972     {
01973         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
01974             "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
01975         if( !filename.isEmpty() )
01976             htmlStr += "<a href=\"" + QString("file:")
01977                      + KURL::encode_string( filename ) + "\">"
01978                      + i18n("Encapsulated message") + "</a>";
01979         else
01980             htmlStr += i18n("Encapsulated message");
01981         htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
01982     }
01983 
01984     if( block.isEncrypted )
01985     {
01986         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
01987             "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
01988         if( block.isDecryptable )
01989             htmlStr += i18n("Encrypted message");
01990         else {
01991             htmlStr += i18n("Encrypted message (decryption not possible)");
01992             if( !block.errorText.isEmpty() )
01993                 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
01994         }
01995         htmlStr += "</td></tr><tr class=\"encrB\"><td>";
01996     }
01997 
01998     if( block.isSigned ) {
01999         QStringList& blockAddrs( block.signerMailAddresses );
02000         // note: At the moment frameColor and showKeyInfos are
02001         //       used for CMS only but not for PGP signatures
02002         // pending(khz): Implement usage of these for PGP sigs as well.
02003         int frameColor = SIG_FRAME_COL_UNDEF;
02004         bool showKeyInfos;
02005         bool onlyShowKeyURL = false;
02006         bool cannotCheckSignature = true;
02007         QString statusStr = sigStatusToString( cryptPlug,
02008                                                block.status_code,
02009                                                block.sigStatusFlags,
02010                                                frameColor,
02011                                                showKeyInfos );
02012         // if needed fallback to english status text
02013         // that was reported by the plugin
02014         if( statusStr.isEmpty() )
02015             statusStr = block.status;
02016         if( block.technicalProblem )
02017             frameColor = SIG_FRAME_COL_YELLOW;
02018 
02019         switch( frameColor ){
02020             case SIG_FRAME_COL_RED:
02021                 cannotCheckSignature = false;
02022                 break;
02023             case SIG_FRAME_COL_YELLOW:
02024                 cannotCheckSignature = true;
02025                 break;
02026             case SIG_FRAME_COL_GREEN:
02027                 cannotCheckSignature = false;
02028                 break;
02029         }
02030 
02031         // compose the string for displaying the key ID
02032         // either as URL or not linked (for PGP)
02033         // note: Once we can start PGP key manager programs
02034         //       from within KMail we could change this and
02035         //       always show the URL.    (khz, 2002/06/27)
02036         QString startKeyHREF;
02037         if( isSMIME )
02038             startKeyHREF =
02039                 QString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02040                 .arg( cryptPlug->displayName() )
02041                 .arg( cryptPlug->libName() )
02042                 .arg( block.keyId );
02043         QString keyWithWithoutURL
02044             = isSMIME
02045             ? QString("%1%2</a>")
02046                 .arg( startKeyHREF )
02047                 .arg( cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02048             : "0x" + QString::fromUtf8( block.keyId );
02049 
02050 
02051         // temporary hack: always show key infos!
02052         showKeyInfos = true;
02053 
02054         // Sorry for using 'black' as null color but .isValid()
02055         // checking with QColor default c'tor did not work for
02056         // some reason.
02057         if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02058 
02059             // new frame settings for CMS:
02060             // beautify the status string
02061             if( !statusStr.isEmpty() ) {
02062                 statusStr.prepend("<i>");
02063                 statusStr.append( "</i>");
02064             }
02065 
02066             // special color handling: S/MIME uses only green/yellow/red.
02067             switch( frameColor ) {
02068                 case SIG_FRAME_COL_RED:
02069                     block.signClass = "signErr";//"signCMSRed";
02070                     onlyShowKeyURL = true;
02071                     break;
02072                 case SIG_FRAME_COL_YELLOW:
02073                     if( block.technicalProblem )
02074                         block.signClass = "signWarn";
02075                     else
02076                         block.signClass = "signOkKeyBad";//"signCMSYellow";
02077                     break;
02078                 case SIG_FRAME_COL_GREEN:
02079                     block.signClass = "signOkKeyOk";//"signCMSGreen";
02080                     // extra hint for green case
02081                     // that email addresses in DN do not match fromAddress
02082                     QString greenCaseWarning;
02083                     QString msgFrom( KPIM::getEmailAddress(fromAddress) );
02084                     QString certificate;
02085                     if( block.keyId.isEmpty() )
02086                         certificate = "certificate";
02087                     else
02088                         certificate = QString("%1%2</a>")
02089                                       .arg( startKeyHREF )
02090                                       .arg( "certificate" );
02091                     if( !blockAddrs.empty() ){
02092                         if( blockAddrs.grep(
02093                                 msgFrom,
02094                                 false ).isEmpty() ) {
02095                             greenCaseWarning =
02096                                 "<u>" +
02097                                 i18n("Warning:") +
02098                                 "</u> " +
02099                                 i18n("Sender's mail address is not stored "
02100                                      "in the %1 used for signing.").arg(certificate) +
02101                                 "<br />" +
02102                                 i18n("sender: ") +
02103                                 msgFrom +
02104                                 "<br />" +
02105                                 i18n("stored: ");
02106                             // We cannot use Qt's join() function here but
02107                             // have to join the addresses manually to
02108                             // extract the mail addresses (without '<''>')
02109                             // before including it into our string:
02110                             bool bStart = true;
02111                             for(QStringList::ConstIterator it = blockAddrs.begin();
02112                                 it != blockAddrs.end(); ++it ){
02113                                 if( !bStart )
02114                                     greenCaseWarning.append(", <br />&nbsp; &nbsp;");
02115                                 bStart = false;
02116                                 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02117                             }
02118                         }
02119                     } else {
02120                         greenCaseWarning =
02121                             "<u>" +
02122                             i18n("Warning:") +
02123                             "</u> " +
02124                             i18n("No mail address is stored in the %1 used for signing, "
02125                                  "so we cannot compare it to the sender's address %2.")
02126                             .arg(certificate)
02127                             .arg(msgFrom);
02128                     }
02129                     if( !greenCaseWarning.isEmpty() ) {
02130                         if( !statusStr.isEmpty() )
02131                             statusStr.append("<br />&nbsp;<br />");
02132                         statusStr.append( greenCaseWarning );
02133                     }
02134                     break;
02135             }
02136 
02137             htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02138                 "class=\"" + block.signClass + "\">"
02139                 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02140             if( block.technicalProblem ) {
02141                 htmlStr += block.errorText;
02142             }
02143             else if( showKeyInfos ) {
02144                 if( cannotCheckSignature ) {
02145                     htmlStr += i18n( "Not enough information to check "
02146                                      "signature. %1" )
02147                                 .arg( keyWithWithoutURL );
02148                 }
02149                 else {
02150 
02151                     if (block.signer.isEmpty())
02152                         signer = "";
02153                     else {
02154                         if( !blockAddrs.empty() ){
02155                             QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02156                             signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02157                         }
02158                     }
02159 
02160                     if( block.keyId.isEmpty() ) {
02161                         if( signer.isEmpty() || onlyShowKeyURL )
02162                             htmlStr += i18n( "Message was signed with unknown key." );
02163                         else
02164                             htmlStr += i18n( "Message was signed by %1." )
02165                                     .arg( signer );
02166                     } else {
02167                         bool dateOK = ( 0 < block.creationTime.tm_year &&
02168                                         block.creationTime.tm_year < 3000 );
02169                         QDateTime created;
02170                         if ( dateOK )
02171                           created.setTime_t( mktime(&block.creationTime) );
02172                         if( dateOK && created.isValid() ) {
02173                             if( signer.isEmpty() ) {
02174                                 if( onlyShowKeyURL )
02175                                     htmlStr += i18n( "Message was signed with key %1." )
02176                                                 .arg( keyWithWithoutURL );
02177                                 else
02178                                     htmlStr += i18n( "Message was signed on %1 with key %2." )
02179                                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02180                                                 .arg( keyWithWithoutURL );
02181                             }
02182                             else {
02183                                 if( onlyShowKeyURL )
02184                                     htmlStr += i18n( "Message was signed with key %1." )
02185                                             .arg( keyWithWithoutURL );
02186                                 else
02187                                     htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02188                                             .arg( KGlobal::locale()->formatDateTime( created ) )
02189                                             .arg( keyWithWithoutURL )
02190                                             .arg( signer );
02191                             }
02192                         }
02193                         else {
02194                             if( signer.isEmpty() || onlyShowKeyURL )
02195                                 htmlStr += i18n( "Message was signed with key %1." )
02196                                         .arg( keyWithWithoutURL );
02197                             else
02198                                 htmlStr += i18n( "Message was signed by %2 with key %1." )
02199                                         .arg( keyWithWithoutURL )
02200                                         .arg( signer );
02201                         }
02202                     }
02203                 }
02204                 htmlStr += "<br />";
02205                 if( !statusStr.isEmpty() ) {
02206                     htmlStr += "&nbsp;<br />";
02207                     htmlStr += i18n( "Status: " );
02208                     htmlStr += statusStr;
02209                 }
02210             } else {
02211                 htmlStr += statusStr;
02212             }
02213             htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02214 
02215         } else {
02216 
02217             // old frame settings for PGP:
02218 
02219             if( block.signer.isEmpty() || block.technicalProblem ) {
02220                 block.signClass = "signWarn";
02221                 htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02222                     "class=\"" + block.signClass + "\">"
02223                     "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02224                 if( block.technicalProblem ) {
02225                     htmlStr += block.errorText;
02226                 }
02227                 else {
02228                   if( !block.keyId.isEmpty() ) {
02229                     bool dateOK = ( 0 < block.creationTime.tm_year &&
02230                                     block.creationTime.tm_year < 3000 );
02231                     QDateTime created;
02232                     if ( dateOK )
02233                       created.setTime_t( mktime(&block.creationTime) );
02234                     if( dateOK && created.isValid() )
02235                         htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02236                                 .arg( KGlobal::locale()->formatDateTime( created ) )
02237                                 .arg( keyWithWithoutURL );
02238                     else
02239                         htmlStr += i18n( "Message was signed with unknown key %1." )
02240                                 .arg( keyWithWithoutURL );
02241                   }
02242                   else
02243                     htmlStr += i18n( "Message was signed with unknown key." );
02244                   htmlStr += "<br />";
02245                   htmlStr += i18n( "The validity of the signature cannot be "
02246                                    "verified." );
02247                   if( !statusStr.isEmpty() ) {
02248                     htmlStr += "<br />";
02249                     htmlStr += i18n( "Status: " );
02250                     htmlStr += "<i>";
02251                     htmlStr += statusStr;
02252                     htmlStr += "</i>";
02253                   }
02254                 }
02255                 htmlStr += "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02256             }
02257             else
02258             {
02259                 // HTMLize the signer's user id and create mailto: link
02260                 signer = KMMessage::quoteHtmlChars( signer, true );
02261                 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02262 
02263                 if (block.isGoodSignature) {
02264                     if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02265                         block.signClass = "signOkKeyBad";
02266                     else
02267                         block.signClass = "signOkKeyOk";
02268                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02269                         "class=\"" + block.signClass + "\">"
02270                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02271                     if( !block.keyId.isEmpty() )
02272                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02273                                    .arg( keyWithWithoutURL )
02274                                    .arg( signer );
02275                     else
02276                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02277                     htmlStr += "<br />";
02278 
02279                     switch( block.keyTrust )
02280                     {
02281                         case Kpgp::KPGP_VALIDITY_UNKNOWN:
02282                         htmlStr += i18n( "The signature is valid, but the key's "
02283                                 "validity is unknown." );
02284                         break;
02285                         case Kpgp::KPGP_VALIDITY_MARGINAL:
02286                         htmlStr += i18n( "The signature is valid and the key is "
02287                                 "marginally trusted." );
02288                         break;
02289                         case Kpgp::KPGP_VALIDITY_FULL:
02290                         htmlStr += i18n( "The signature is valid and the key is "
02291                                 "fully trusted." );
02292                         break;
02293                         case Kpgp::KPGP_VALIDITY_ULTIMATE:
02294                         htmlStr += i18n( "The signature is valid and the key is "
02295                                 "ultimately trusted." );
02296                         break;
02297                         default:
02298                         htmlStr += i18n( "The signature is valid, but the key is "
02299                                 "untrusted." );
02300                     }
02301                     htmlStr += "</td></tr>"
02302                         "<tr class=\"" + block.signClass + "B\"><td>";
02303                 }
02304                 else
02305                 {
02306                     block.signClass = "signErr";
02307                     htmlStr += "<table cellspacing=\"1\" "+cellPadding+" "
02308                         "class=\"" + block.signClass + "\">"
02309                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02310                     if( !block.keyId.isEmpty() )
02311                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02312                         .arg( keyWithWithoutURL )
02313                         .arg( signer );
02314                     else
02315                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02316                     htmlStr += "<br />";
02317                     htmlStr += i18n("Warning: The signature is bad.");
02318                     htmlStr += "</td></tr>"
02319                         "<tr class=\"" + block.signClass + "B\"><td>";
02320                 }
02321             }
02322         }
02323     }
02324 
02325     return htmlStr;
02326 }
02327 
02328 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02329 {
02330     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02331 
02332     QString htmlStr;
02333 
02334     if (block.isSigned) {
02335         htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02336         htmlStr += "<td dir=\"" + dir + "\">" +
02337             i18n( "End of signed message" ) +
02338             "</td></tr></table>";
02339     }
02340 
02341     if (block.isEncrypted) {
02342         htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02343                 i18n( "End of encrypted message" ) +
02344             "</td></tr></table>";
02345     }
02346 
02347     if( block.isEncapsulatedRfc822Message )
02348     {
02349         htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02350             i18n( "End of encapsulated message" ) +
02351             "</td></tr></table>";
02352     }
02353 
02354     return htmlStr;
02355 }
02356 
02357 //-----------------------------------------------------------------------------
02358 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02359                                 const QString& fromAddress )
02360 {
02361   KMMsgSignatureState dummy1;
02362   KMMsgEncryptionState dummy2;
02363   writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02364 }
02365 
02366 //-----------------------------------------------------------------------------
02367 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02368                                 const QString& fromAddress,
02369                                 KMMsgSignatureState&  inlineSignatureState,
02370                                 KMMsgEncryptionState& inlineEncryptionState,
02371                                 bool decorate )
02372 {
02373   bool goodSignature = false;
02374   Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02375   assert(pgp != 0);
02376   bool isPgpMessage = false; // true if the message contains at least one
02377                              // PGP MESSAGE or one PGP SIGNED MESSAGE block
02378   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02379   QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02380 
02381   inlineSignatureState  = KMMsgNotSigned;
02382   inlineEncryptionState = KMMsgNotEncrypted;
02383   QPtrList<Kpgp::Block> pgpBlocks;
02384   QStrList nonPgpBlocks;
02385   if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02386   {
02387       bool isEncrypted = false, isSigned = false;
02388       bool fullySignedOrEncrypted = true;
02389       bool firstNonPgpBlock = true;
02390       bool couldDecrypt = false;
02391       QString signer;
02392       QCString keyId;
02393       QString decryptionError;
02394       Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02395 
02396       QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02397 
02398       QStrListIterator npbit( nonPgpBlocks );
02399 
02400       QString htmlStr;
02401       for( ; *pbit != 0; ++pbit, ++npbit )
02402       {
02403           // insert the next Non-OpenPGP block
02404           QCString str( *npbit );
02405           if( !str.isEmpty() ) {
02406             htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02407             kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
02408                             << "'" << endl;
02409             // treat messages with empty lines before the first clearsigned
02410             // block as fully signed/encrypted
02411             if( firstNonPgpBlock ) {
02412               // check whether str only consists of \n
02413               for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02414                 if( *c != '\n' ) {
02415                   fullySignedOrEncrypted = false;
02416                   break;
02417                 }
02418               }
02419             }
02420             else {
02421               fullySignedOrEncrypted = false;
02422             }
02423           }
02424           firstNonPgpBlock = false;
02425 
02426           //htmlStr += "<br>";
02427 
02428           Kpgp::Block* block = *pbit;
02429           if( ( block->type() == Kpgp::PgpMessageBlock &&
02430                 // ### Workaround for bug 56693
02431                 !kmkernel->contextMenuShown() ) ||
02432               ( block->type() == Kpgp::ClearsignedBlock ) )
02433           {
02434               isPgpMessage = true;
02435               if( block->type() == Kpgp::PgpMessageBlock )
02436               {
02437                 if ( mReader )
02438                   emit mReader->noDrag();
02439                 // try to decrypt this OpenPGP block
02440                 couldDecrypt = block->decrypt();
02441                 isEncrypted = block->isEncrypted();
02442                 if (!couldDecrypt) {
02443                   decryptionError = pgp->lastErrorMsg();
02444                 }
02445               }
02446               else
02447               {
02448                   // try to verify this OpenPGP block
02449                   block->verify();
02450               }
02451 
02452               isSigned = block->isSigned();
02453               if( isSigned )
02454               {
02455                   keyId = block->signatureKeyId();
02456                   signer = block->signatureUserId();
02457                   if( !signer.isEmpty() )
02458                   {
02459                       goodSignature = block->goodSignature();
02460 
02461                       if( !keyId.isEmpty() ) {
02462                         keyTrust = pgp->keyTrust( keyId );
02463                         Kpgp::Key* key = pgp->publicKey( keyId );
02464                         if ( key ) {
02465                           // Use the user ID from the key because this one
02466                           // is charset safe.
02467                           signer = key->primaryUserID();
02468                         }
02469                       }
02470                       else
02471                         // This is needed for the PGP 6 support because PGP 6 doesn't
02472                         // print the key id of the signing key if the key is known.
02473                         keyTrust = pgp->keyTrust( signer );
02474                   }
02475               }
02476 
02477               if( isSigned )
02478                 inlineSignatureState = KMMsgPartiallySigned;
02479               if( isEncrypted )
02480                 inlineEncryptionState = KMMsgPartiallyEncrypted;
02481 
02482               PartMetaData messagePart;
02483 
02484               messagePart.isSigned = isSigned;
02485               messagePart.technicalProblem = false;
02486               messagePart.isGoodSignature = goodSignature;
02487               messagePart.isEncrypted = isEncrypted;
02488               messagePart.isDecryptable = couldDecrypt;
02489               messagePart.decryptionError = decryptionError;
02490               messagePart.signer = signer;
02491               messagePart.keyId = keyId;
02492               messagePart.keyTrust = keyTrust;
02493 
02494               htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02495 
02496               htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02497               htmlStr += writeSigstatFooter( messagePart );
02498           }
02499           else // block is neither message block nor clearsigned block
02500             htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02501                                    decorate );
02502       }
02503 
02504       // add the last Non-OpenPGP block
02505       QCString str( nonPgpBlocks.last() );
02506       if( !str.isEmpty() ) {
02507         htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02508         // Even if the trailing Non-OpenPGP block isn't empty we still
02509         // consider the message part fully signed/encrypted because else
02510         // all inline signed mailing list messages would only be partially
02511         // signed because of the footer which is often added by the mailing
02512         // list software. IK, 2003-02-15
02513       }
02514       if( fullySignedOrEncrypted ) {
02515         if( inlineSignatureState == KMMsgPartiallySigned )
02516           inlineSignatureState = KMMsgFullySigned;
02517         if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02518           inlineEncryptionState = KMMsgFullyEncrypted;
02519       }
02520       htmlWriter()->queue( htmlStr );
02521   }
02522   else
02523     htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02524 }
02525 
02526 
02527 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
02528 {
02529   assert( mReader );
02530   assert( cssHelper() );
02531 
02532   int convertFlags = LinkLocator::PreserveSpaces;
02533   if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02534     convertFlags |= LinkLocator::ReplaceSmileys;
02535   }
02536   QString htmlStr;
02537   const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02538   QString quoteFontTag[3];
02539   QString deepQuoteFontTag[3];
02540   for ( int i = 0 ; i < 3 ; ++i ) {
02541     quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02542     deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02543   }
02544   const QString normalEndTag = "</div>";
02545   const QString quoteEnd = "</div>";
02546 
02547   unsigned int pos, beg;
02548   const unsigned int length = s.length();
02549 
02550   // skip leading empty lines
02551   for ( pos = 0; pos < length && s[pos] <= ' '; pos++ );
02552   while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02553   beg = pos;
02554 
02555   int currQuoteLevel = -2; // -2 == no previous lines
02556   bool curHidden = false; // no hide any block
02557 
02558   while (beg<length)
02559   {
02560     QString line;
02561 
02562     /* search next occurrence of '\n' */
02563     pos = s.find('\n', beg, FALSE);
02564     if (pos == (unsigned int)(-1))
02565         pos = length;
02566 
02567     line = s.mid(beg,pos-beg);
02568     beg = pos+1;
02569 
02570     /* calculate line's current quoting depth */
02571     int actQuoteLevel = -1;
02572 
02573     if ( GlobalSettings::self()->showExpandQuotesMark() )
02574     {
02575       // Cache Icons
02576       if ( mCollapseIcon.isEmpty() ) {
02577         mCollapseIcon= LinkLocator::pngToDataUrl(
02578             KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
02579       }
02580       if ( mExpandIcon.isEmpty() )
02581         mExpandIcon= LinkLocator::pngToDataUrl(
02582             KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
02583     }
02584 
02585     for (unsigned int p=0; p<line.length(); p++) {
02586       switch (line[p].latin1()) {
02587         case '>':
02588         case '|':
02589           actQuoteLevel++;
02590           break;
02591         case ' ':  // spaces and tabs are allowed between the quote markers
02592         case '\t':
02593         case '\r':
02594           break;
02595         default:  // stop quoting depth calculation
02596           p = line.length();
02597           break;
02598       }
02599     } /* for() */
02600 
02601     bool actHidden = false;
02602     QString textExpand;
02603 
02604     // This quoted line needs be hiden
02605     if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
02606         && mReader->mLevelQuote <= ( actQuoteLevel ) )
02607       actHidden = true;
02608 
02609     if ( actQuoteLevel != currQuoteLevel ) {
02610       /* finish last quotelevel */
02611       if (currQuoteLevel == -1)
02612         htmlStr.append( normalEndTag );
02613       else if ( currQuoteLevel >= 0 && !curHidden )
02614         htmlStr.append( quoteEnd );
02615 
02616       /* start new quotelevel */
02617       if (actQuoteLevel == -1)
02618         htmlStr += normalStartTag;
02619       else
02620       {
02621         if ( GlobalSettings::self()->showExpandQuotesMark() )
02622         {
02623           if (  actHidden )
02624           {
02625             //only show the QuoteMark when is the first line of the level hidden
02626             if ( !curHidden )
02627             {
02628               //Expand all quotes
02629               htmlStr += "<div class=\"quotelevelmark\" >" ;
02630               htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02631                   "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02632                 .arg(-1)
02633                 .arg( mExpandIcon );
02634               htmlStr += "</div><br/>";
02635               htmlStr += quoteEnd;
02636             }
02637           }else {
02638             htmlStr += "<div class=\"quotelevelmark\" >" ;
02639             htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
02640                 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
02641               .arg(actQuoteLevel)
02642               .arg( mCollapseIcon);
02643             htmlStr += "</div>";
02644             if ( actQuoteLevel < 3 )
02645               htmlStr += quoteFontTag[actQuoteLevel];
02646             else
02647               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02648           }
02649         } else
02650             if ( actQuoteLevel < 3 )
02651               htmlStr += quoteFontTag[actQuoteLevel];
02652             else
02653               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
02654       }
02655       currQuoteLevel = actQuoteLevel;
02656     }
02657     curHidden = actHidden;
02658 
02659 
02660     if ( !actHidden )
02661     {
02662       // don't write empty <div ...></div> blocks (they have zero height)
02663       // ignore ^M DOS linebreaks
02664       if( !line.replace('\015', "").isEmpty() )
02665       {
02666          htmlStr +=QString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
02667          htmlStr += LinkLocator::convertToHtml( line, convertFlags );
02668          htmlStr += QString( "</div>" );
02669       }
02670       else
02671         htmlStr += "<br>";
02672     }
02673   } /* while() */
02674 
02675   /* really finish the last quotelevel */
02676   if (currQuoteLevel == -1)
02677      htmlStr.append( normalEndTag );
02678   else
02679      htmlStr.append( quoteEnd );
02680 
02681   //kdDebug(5006) << "KMReaderWin::quotedHTML:\n"
02682   //              << "========================================\n"
02683   //              << htmlStr
02684   //              << "\n======================================\n";
02685   return htmlStr;
02686 }
02687 
02688 
02689 
02690   const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
02691     assert( node );
02692     if ( mReader && mReader->overrideCodec() )
02693       return mReader->overrideCodec();
02694     return node->msgPart().codec();
02695   }
02696 
02697 #ifdef MARCS_DEBUG
02698   void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
02699                                      size_t len ) {
02700     assert( filename );
02701 
02702     QFile f( filename );
02703     if ( f.open( IO_WriteOnly ) ) {
02704       if ( start ) {
02705         QDataStream ds( &f );
02706         ds.writeRawBytes( start, len );
02707       }
02708       f.close();  // If data is 0 we just create a zero length file.
02709     }
02710   }
02711 #endif // !NDEBUG
02712 
02713 
02714 } // namespace KMail
KDE Home | KDE Accessibility Home | Description of Access Keys