kmail

objecttreeparser.cpp

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