kmail

objecttreeparser.cpp

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