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