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