kmail

objecttreeparser.cpp

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