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   bool ObjectTreeParser::processToltecMail( partNode *node )
01214   {
01215     if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
01216          !node->isToltecMessage() || mShowRawToltecMail )
01217       return false;
01218 
01219     htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
01220     htmlWriter()->queue( "<br><br><a href=\"kmail:showRawToltecMail\">" +
01221                          i18n( "Show Raw Message" ) + "</a>" );
01222     return true;
01223   }
01224 
01225   bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
01226 
01227     if ( processToltecMail( node ) ) {
01228       return true;
01229     }
01230 
01231     partNode * child = node->firstChild();
01232     if ( !child )
01233       return false;
01234 
01235     // normal treatment of the parts in the mp/mixed container
01236     stdChildHandling( child );
01237     return true;
01238   }
01239 
01240   bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
01241     partNode * child = node->firstChild();
01242     if ( !child )
01243       return false;
01244 
01245     partNode * dataHtml = child->findType( DwMime::kTypeText,
01246                                            DwMime::kSubtypeHtml, false, true );
01247     partNode * dataPlain = child->findType( DwMime::kTypeText,
01248                                             DwMime::kSubtypePlain, false, true );
01249 
01250     if ( (mReader && mReader->htmlMail() && dataHtml) ||
01251          (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
01252       if ( dataPlain )
01253         dataPlain->setProcessed( true, false );
01254       stdChildHandling( dataHtml );
01255       return true;
01256     }
01257 
01258     if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
01259       if ( dataHtml )
01260         dataHtml->setProcessed( true, false );
01261       stdChildHandling( dataPlain );
01262       return true;
01263     }
01264 
01265     stdChildHandling( child );
01266     return true;
01267   }
01268 
01269   bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
01270     return processMultiPartMixedSubtype( node, result );
01271   }
01272 
01273   bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
01274     return processMultiPartMixedSubtype( node, result );
01275   }
01276 
01277   bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
01278     if ( node->childCount() != 2 ) {
01279       kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
01280                     << "processing as multipart/mixed" << endl;
01281       if ( node->firstChild() )
01282         stdChildHandling( node->firstChild() );
01283       return node->firstChild();
01284     }
01285 
01286     partNode * signedData = node->firstChild();
01287     assert( signedData );
01288 
01289     partNode * signature = signedData->nextSibling();
01290     assert( signature );
01291 
01292     signature->setProcessed( true, true );
01293 
01294     if ( !includeSignatures() ) {
01295       stdChildHandling( signedData );
01296       return true;
01297     }
01298 
01299     // FIXME(marc) check here that the protocol parameter matches the
01300     // mimetype of "signature" (not required by the RFC, but practised
01301     // by all implementaions of security multiparts
01302 
01303     const QString contentType = node->contentTypeParameter( "protocol" ).lower();
01304     const Kleo::CryptoBackend::Protocol *protocol = 0;
01305     if ( contentType == "application/pkcs7-signature" || contentType == "application/x-pkcs7-signature" )
01306       protocol = Kleo::CryptoBackendFactory::instance()->smime();
01307     else if ( contentType == "application/pgp-signature" || contentType == "application/x-pgp-signature" )
01308       protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
01309 
01310     if ( !protocol ) {
01311       signature->setProcessed( true, true );
01312       stdChildHandling( signedData );
01313       return true;
01314     }
01315 
01316     CryptoProtocolSaver saver( this, protocol );
01317 
01318     node->setSignatureState( KMMsgFullySigned );
01319     writeOpaqueOrMultipartSignedData( signedData, *signature,
01320                                       node->trueFromAddress() );
01321     return true;
01322   }
01323 
01324   bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
01325     partNode * child = node->firstChild();
01326     if ( !child )
01327       return false;
01328 
01329     if ( keepEncryptions() ) {
01330       node->setEncryptionState( KMMsgFullyEncrypted );
01331       const QCString cstr = node->msgPart().bodyDecoded();
01332       if ( mReader )
01333         writeBodyString( cstr, node->trueFromAddress(),
01334                          codecFor( node ), result, false );
01335       mRawReplyString += cstr;
01336       return true;
01337     }
01338 
01339     const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
01340 
01341     /*
01342       ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
01343     */
01344     partNode * data = child->findType( DwMime::kTypeApplication,
01345                                        DwMime::kSubtypeOctetStream, false, true );
01346     if ( data ) {
01347       useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
01348     }
01349     if ( !data ) {
01350       data = child->findType( DwMime::kTypeApplication,
01351                               DwMime::kSubtypePkcs7Mime, false, true );
01352       if ( data ) {
01353         useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
01354       }
01355     }
01356     /*
01357       ---------------------------------------------------------------------------------------------------------------
01358     */
01359 
01360     if ( !data ) {
01361       stdChildHandling( child );
01362       return true;
01363     }
01364 
01365     CryptoProtocolSaver cpws( this, useThisCryptProto );
01366 
01367     if ( partNode * dataChild = data->firstChild() ) {
01368       //kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01369       stdChildHandling( dataChild );
01370       //kdDebug(5006) << "\n----->  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01371       return true;
01372     }
01373 
01374     node->setEncryptionState( KMMsgFullyEncrypted );
01375 
01376     if ( mReader && !mReader->decryptMessage() ) {
01377       writeDeferredDecryptionBlock();
01378       data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
01379       return true;
01380     }
01381 
01382     //kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01383     PartMetaData messagePart;
01384     QCString decryptedData;
01385     bool signatureFound;
01386     std::vector<GpgME::Signature> signatures;
01387     bool passphraseError;
01388     bool actuallyEncrypted = true;
01389     bool decryptionStarted;
01390 
01391     bool bOkDecrypt = okDecryptMIME( *data,
01392                                      decryptedData,
01393                                      signatureFound,
01394                                      signatures,
01395                                      true,
01396                                      passphraseError,
01397                                      actuallyEncrypted,
01398                                      decryptionStarted,
01399                                      messagePart.errorText,
01400                                      messagePart.auditLogError,
01401                                      messagePart.auditLog );
01402 
01403     if ( decryptionStarted ) {
01404       writeDecryptionInProgressBlock();
01405       return true;
01406     }
01407 
01408     // paint the frame
01409     if ( mReader ) {
01410       messagePart.isDecryptable = bOkDecrypt;
01411       messagePart.isEncrypted = true;
01412       messagePart.isSigned = false;
01413       htmlWriter()->queue( writeSigstatHeader( messagePart,
01414                                                cryptoProtocol(),
01415                                                node->trueFromAddress() ) );
01416     }
01417 
01418     if ( bOkDecrypt ) {
01419       // Note: Multipart/Encrypted might also be signed
01420       //       without encapsulating a nicely formatted
01421       //       ~~~~~~~                 Multipart/Signed part.
01422       //                               (see RFC 3156 --> 6.2)
01423       // In this case we paint a _2nd_ frame inside the
01424       // encryption frame, but we do _not_ show a respective
01425       // encapsulated MIME part in the Mime Tree Viewer
01426       // since we do want to show the _true_ structure of the
01427       // message there - not the structure that the sender's
01428       // MUA 'should' have sent.  :-D       (khz, 12.09.2002)
01429       //
01430       if ( signatureFound ) {
01431         writeOpaqueOrMultipartSignedData( 0,
01432                                           *node,
01433                                           node->trueFromAddress(),
01434                                           false,
01435                                           &decryptedData,
01436                                           signatures,
01437                                           false );
01438         node->setSignatureState( KMMsgFullySigned );
01439       } else {
01440         insertAndParseNewChildNode( *node,
01441                                     &*decryptedData,
01442                                     "encrypted data" );
01443       }
01444     } else {
01445       mRawReplyString += decryptedData;
01446       if ( mReader ) {
01447         // print the error message that was returned in decryptedData
01448         // (utf8-encoded)
01449         htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01450       }
01451     }
01452 
01453     if ( mReader )
01454       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01455     data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
01456     return true;
01457   }
01458 
01459 
01460   bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
01461     if ( mReader
01462          && !attachmentStrategy()->inlineNestedMessages()
01463          && !showOnlyOneMimePart() )
01464       return false;
01465 
01466     if ( partNode * child = node->firstChild() ) {
01467       //kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01468       ObjectTreeParser otp( mReader, cryptoProtocol() );
01469       otp.parseObjectTree( child );
01470       mRawReplyString += otp.rawReplyString();
01471       mTextualContent += otp.textualContent();
01472       if ( !otp.textualContentCharset().isEmpty() )
01473         mTextualContentCharset = otp.textualContentCharset();
01474       //kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01475       return true;
01476     }
01477     //kdDebug(5006) << "\n----->  Initially processing data of embedded RfC 822 message\n" << endl;
01478     // paint the frame
01479     PartMetaData messagePart;
01480     if ( mReader ) {
01481       messagePart.isEncrypted = false;
01482       messagePart.isSigned = false;
01483       messagePart.isEncapsulatedRfc822Message = true;
01484       QString filename =
01485         mReader->writeMessagePartToTempFile( &node->msgPart(),
01486                                             node->nodeId() );
01487       htmlWriter()->queue( writeSigstatHeader( messagePart,
01488                                                cryptoProtocol(),
01489                                                node->trueFromAddress(),
01490                                                node ) );
01491     }
01492     QCString rfc822messageStr( node->msgPart().bodyDecoded() );
01493     // display the headers of the encapsulated message
01494     DwMessage* rfc822DwMessage = new DwMessage(); // will be deleted by c'tor of rfc822headers
01495     rfc822DwMessage->FromString( rfc822messageStr );
01496     rfc822DwMessage->Parse();
01497     KMMessage rfc822message( rfc822DwMessage );
01498     node->setFromAddress( rfc822message.from() );
01499     //kdDebug(5006) << "\n----->  Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl;
01500     if ( mReader )
01501       htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
01502       //mReader->parseMsgHeader( &rfc822message );
01503     // display the body of the encapsulated message
01504     insertAndParseNewChildNode( *node,
01505                                 &*rfc822messageStr,
01506                                 "encapsulated message", false /*append*/,
01507                                 false /*add to textual content*/ );
01508     node->setDisplayedEmbedded( true );
01509     if ( mReader )
01510       htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01511     return true;
01512   }
01513 
01514 
01515   bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
01516     if ( partNode * child = node->firstChild() ) {
01517       //kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01518       ObjectTreeParser otp( mReader, cryptoProtocol() );
01519       otp.parseObjectTree( child );
01520       mRawReplyString += otp.rawReplyString();
01521       mTextualContent += otp.textualContent();
01522       if ( !otp.textualContentCharset().isEmpty() )
01523         mTextualContentCharset = otp.textualContentCharset();
01524       //kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01525       return true;
01526     }
01527 
01528     const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
01529     if (    node->parentNode()
01530             && DwMime::kTypeMultipart    == node->parentNode()->type()
01531             && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
01532       //kdDebug(5006) << "\n----->  Initially processing encrypted data\n" << endl;
01533       node->setEncryptionState( KMMsgFullyEncrypted );
01534       if ( keepEncryptions() ) {
01535         const QCString cstr = node->msgPart().bodyDecoded();
01536         if ( mReader )
01537           writeBodyString( cstr, node->trueFromAddress(),
01538                            codecFor( node ), result, false );
01539         mRawReplyString += cstr;
01540       } else if ( mReader && !mReader->decryptMessage() ) {
01541         writeDeferredDecryptionBlock();
01542       } else {
01543         /*
01544           ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
01545         */
01546         PartMetaData messagePart;
01547         setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
01548         QCString decryptedData;
01549         bool signatureFound;
01550         std::vector<GpgME::Signature> signatures;
01551         bool passphraseError;
01552         bool actuallyEncrypted = true;
01553         bool decryptionStarted;
01554 
01555         bool bOkDecrypt = okDecryptMIME( *node,
01556                                          decryptedData,
01557                                          signatureFound,
01558                                          signatures,
01559                                          true,
01560                                          passphraseError,
01561                                          actuallyEncrypted,
01562                                          decryptionStarted,
01563                                          messagePart.errorText,
01564                                          messagePart.auditLogError,
01565                                          messagePart.auditLog );
01566 
01567         if ( decryptionStarted ) {
01568           writeDecryptionInProgressBlock();
01569           return true;
01570         }
01571 
01572         // paint the frame
01573         if ( mReader ) {
01574           messagePart.isDecryptable = bOkDecrypt;
01575           messagePart.isEncrypted = true;
01576           messagePart.isSigned = false;
01577           htmlWriter()->queue( writeSigstatHeader( messagePart,
01578                                                    cryptoProtocol(),
01579                                                    node->trueFromAddress() ) );
01580         }
01581 
01582         if ( bOkDecrypt ) {
01583           // fixing the missing attachments bug #1090-b
01584           insertAndParseNewChildNode( *node,
01585                                       &*decryptedData,
01586                                       "encrypted data" );
01587         } else {
01588           mRawReplyString += decryptedData;
01589           if ( mReader ) {
01590             // print the error message that was returned in decryptedData
01591             // (utf8-encoded)
01592             htmlWriter()->queue( QString::fromUtf8( decryptedData.data() ) );
01593           }
01594         }
01595 
01596         if ( mReader )
01597           htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01598       }
01599       return true;
01600     }
01601     setCryptoProtocol( oldUseThisCryptPlug );
01602     return false;
01603   }
01604 
01605   bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
01606     if ( partNode * child = node->firstChild() ) {
01607       //kdDebug(5006) << "\n----->  Calling parseObjectTree( curNode->mChild )\n" << endl;
01608       ObjectTreeParser otp( mReader, cryptoProtocol() );
01609       otp.parseObjectTree( child );
01610       mRawReplyString += otp.rawReplyString();
01611       mTextualContent += otp.textualContent();
01612       if ( !otp.textualContentCharset().isEmpty() )
01613         mTextualContentCharset = otp.textualContentCharset();
01614       //kdDebug(5006) << "\n<-----  Returning from parseObjectTree( curNode->mChild )\n" << endl;
01615       return true;
01616     }
01617 
01618     //kdDebug(5006) << "\n----->  Initially processing signed and/or encrypted data\n" << endl;
01619     if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
01620       return false;
01621 
01622     const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
01623 
01624     const QString smimeType = node->contentTypeParameter("smime-type").lower();
01625 
01626     if ( smimeType == "certs-only" ) {
01627       result.setNeverDisplayInline( true );
01628       if ( !smimeCrypto || !mReader )
01629         return false;
01630 
01631       const KConfigGroup reader( KMKernel::config(), "Reader" );
01632       if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
01633         return false;
01634 
01635       const QByteArray certData = node->msgPart().bodyDecodedBinary();
01636 
01637       const STD_NAMESPACE_PREFIX auto_ptr<Kleo::ImportJob> import( smimeCrypto->importJob() );
01638       const GpgME::ImportResult res = import->exec( certData );
01639       if ( res.error() ) {
01640         htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
01641                                    "Reason: %1").arg( QString::fromLocal8Bit( res.error().asString() ) ) );
01642         return true;
01643       }
01644 
01645       const int nImp = res.numImported();
01646       const int nUnc = res.numUnchanged();
01647       const int nSKImp = res.numSecretKeysImported();
01648       const int nSKUnc = res.numSecretKeysUnchanged();
01649       if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
01650         htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
01651         return true;
01652       }
01653       QString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br>&nbsp;<br>";
01654       if ( nImp )
01655         comment += i18n( "1 new certificate was imported.",
01656                          "%n new certificates were imported.", nImp ) + "<br>";
01657       if ( nUnc )
01658         comment += i18n( "1 certificate was unchanged.",
01659                          "%n certificates were unchanged.", nUnc ) + "<br>";
01660       if ( nSKImp )
01661         comment += i18n( "1 new secret key was imported.",
01662                          "%n new secret keys were imported.", nSKImp ) + "<br>";
01663       if ( nSKUnc )
01664         comment += i18n( "1 secret key was unchanged.",
01665                          "%n secret keys were unchanged.", nSKUnc ) + "<br>";
01666       comment += "&nbsp;<br>";
01667       htmlWriter()->queue( comment );
01668       if ( !nImp && !nSKImp ) {
01669         htmlWriter()->queue( "<hr>" );
01670         return true;
01671       }
01672       const std::vector<GpgME::Import> imports = res.imports();
01673       if ( imports.empty() ) {
01674         htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
01675         return true;
01676       }
01677       htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
01678       for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
01679         if ( (*it).error() )
01680           htmlWriter()->queue( i18n( "Failed: %1 (%2)" )
01681                                .arg( (*it).fingerprint(),
01682                                      QString::fromLocal8Bit( (*it).error().asString() ) ) );
01683         else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
01684           if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
01685             htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
01686           }
01687           else {
01688             htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
01689           }
01690         }
01691         htmlWriter()->queue( "<br>" );
01692       }
01693 
01694       htmlWriter()->queue( "<hr>" );
01695       return true;
01696     }
01697 
01698     if ( !smimeCrypto )
01699       return false;
01700     CryptoProtocolSaver cpws( this, smimeCrypto );
01701 
01702     bool isSigned      = smimeType == "signed-data";
01703     bool isEncrypted   = smimeType == "enveloped-data";
01704 
01705     // Analyze "signTestNode" node to find/verify a signature.
01706     // If zero this verification was successfully done after
01707     // decrypting via recursion by insertAndParseNewChildNode().
01708     partNode* signTestNode = isEncrypted ? 0 : node;
01709 
01710 
01711     // We try decrypting the content
01712     // if we either *know* that it is an encrypted message part
01713     // or there is neither signed nor encrypted parameter.
01714     if ( !isSigned ) {
01715       if ( isEncrypted ) {
01716         //kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data" << endl;
01717       }
01718       else {
01719         //kdDebug(5006) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?" << endl;
01720       }
01721       QCString decryptedData;
01722       PartMetaData messagePart;
01723       messagePart.isEncrypted = true;
01724       messagePart.isSigned = false;
01725       bool signatureFound;
01726       std::vector<GpgME::Signature> signatures;
01727       bool passphraseError;
01728       bool actuallyEncrypted = true;
01729       bool decryptionStarted;
01730 
01731       if ( mReader && !mReader->decryptMessage() ) {
01732         writeDeferredDecryptionBlock();
01733         isEncrypted = true;
01734         signTestNode = 0; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
01735       } else {
01736         const bool bOkDecrypt = okDecryptMIME( *node,
01737                           decryptedData,
01738                           signatureFound,
01739                           signatures,
01740                           false,
01741                           passphraseError,
01742                           actuallyEncrypted,
01743                           decryptionStarted,
01744                           messagePart.errorText,
01745                           messagePart.auditLogError,
01746                           messagePart.auditLog );
01747         if ( decryptionStarted ) {
01748           writeDecryptionInProgressBlock();
01749           return true;
01750         }
01751         if ( bOkDecrypt ) {
01752           //kdDebug(5006) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !" << endl;
01753           isEncrypted = true;
01754           node->setEncryptionState( KMMsgFullyEncrypted );
01755           signTestNode = 0;
01756           // paint the frame
01757           messagePart.isDecryptable = true;
01758           if ( mReader )
01759             htmlWriter()->queue( writeSigstatHeader( messagePart,
01760                                                      cryptoProtocol(),
01761                                                      node->trueFromAddress() ) );
01762           insertAndParseNewChildNode( *node,
01763                                       &*decryptedData,
01764                                       "encrypted data" );
01765           if ( mReader )
01766             htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01767         } else {
01768           // decryption failed, which could be because the part was encrypted but
01769           // decryption failed, or because we didn't know if it was encrypted, tried,
01770           // and failed. If the message was not actually encrypted, we continue
01771           // assuming it's signed
01772           if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
01773             isEncrypted = true;
01774             signTestNode = 0;
01775           }
01776 
01777           if ( isEncrypted ) {
01778             //kdDebug(5006) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !" << endl;
01779             // paint the frame
01780             messagePart.isDecryptable = false;
01781             if ( mReader ) {
01782               htmlWriter()->queue( writeSigstatHeader( messagePart,
01783                                                        cryptoProtocol(),
01784                                                        node->trueFromAddress() ) );
01785               assert( mReader->decryptMessage() ); // handled above
01786               writePartIcon( &node->msgPart(), node->nodeId() );
01787               htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01788             }
01789           } else {
01790             //kdDebug(5006) << "pkcs7 mime  -  NO encryption found" << endl;
01791           }
01792         }
01793       }
01794       if ( isEncrypted )
01795         node->setEncryptionState( KMMsgFullyEncrypted );
01796     }
01797 
01798     // We now try signature verification if necessarry.
01799     if ( signTestNode ) {
01800       if ( isSigned ) {
01801         //kdDebug(5006) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data" << endl;
01802       }
01803       else {
01804         //kdDebug(5006) << "pkcs7 mime  -  type unknown  -  opaque signed data ?" << endl;
01805       }
01806 
01807       bool sigFound = writeOpaqueOrMultipartSignedData( 0,
01808                                                         *signTestNode,
01809                                                         node->trueFromAddress(),
01810                                                         true,
01811                                                         0,
01812                                                         std::vector<GpgME::Signature>(),
01813                                                         isEncrypted );
01814       if ( sigFound ) {
01815         if ( !isSigned ) {
01816           //kdDebug(5006) << "pkcs7 mime  -  signature found  -  opaque signed data !" << endl;
01817           isSigned = true;
01818         }
01819         signTestNode->setSignatureState( KMMsgFullySigned );
01820         if ( signTestNode != node )
01821           node->setSignatureState( KMMsgFullySigned );
01822       } else {
01823         //kdDebug(5006) << "pkcs7 mime  -  NO signature found   :-(" << endl;
01824       }
01825     }
01826 
01827     return isSigned || isEncrypted;
01828 }
01829 
01830 bool ObjectTreeParser::decryptChiasmus( const QByteArray& data, QByteArray& bodyDecoded, QString& errorText )
01831 {
01832   const Kleo::CryptoBackend::Protocol * chiasmus =
01833     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
01834   Q_ASSERT( chiasmus );
01835   if ( !chiasmus )
01836     return false;
01837 
01838   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
01839   if ( !listjob.get() ) {
01840     errorText = i18n( "Chiasmus backend does not offer the "
01841                       "\"x-obtain-keys\" function. Please report this bug." );
01842     return false;
01843   }
01844 
01845   if ( listjob->exec() ) {
01846     errorText = i18n( "Chiasmus Backend Error" );
01847     return false;
01848   }
01849 
01850   const QVariant result = listjob->property( "result" );
01851   if ( result.type() != QVariant::StringList ) {
01852     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01853                       "The \"x-obtain-keys\" function did not return a "
01854                       "string list. Please report this bug." );
01855     return false;
01856   }
01857 
01858   const QStringList keys = result.toStringList();
01859   if ( keys.empty() ) {
01860     errorText = i18n( "No keys have been found. Please check that a "
01861                       "valid key path has been set in the Chiasmus "
01862                       "configuration." );
01863     return false;
01864   }
01865 
01866   emit mReader->noDrag();
01867   ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
01868                                    keys, GlobalSettings::chiasmusDecryptionKey(),
01869                                    GlobalSettings::chiasmusDecryptionOptions() );
01870   if ( selectorDlg.exec() != QDialog::Accepted )
01871     return false;
01872 
01873   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
01874   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
01875   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
01876 
01877   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() ) );
01878   if ( !job.get() ) {
01879     errorText = i18n( "Chiasmus backend does not offer the "
01880                       "\"x-decrypt\" function. Please report this bug." );
01881     return false;
01882   }
01883 
01884   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
01885        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
01886        !job->setProperty( "input", data ) ) {
01887     errorText = i18n( "The \"x-decrypt\" function does not accept "
01888                       "the expected parameters. Please report this bug." );
01889     return false;
01890   }
01891 
01892   if ( job->exec() ) {
01893     errorText = i18n( "Chiasmus Decryption Error" );
01894     return false;
01895   }
01896 
01897   const QVariant resultData = job->property( "result" );
01898   if ( resultData.type() != QVariant::ByteArray ) {
01899     errorText = i18n( "Unexpected return value from Chiasmus backend: "
01900                       "The \"x-decrypt\" function did not return a "
01901                       "byte array. Please report this bug." );
01902     return false;
01903   }
01904   bodyDecoded = resultData.toByteArray();
01905   return true;
01906 }
01907 
01908 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
01909 {
01910   if ( !mReader ) {
01911     mRawReplyString = curNode->msgPart().bodyDecoded();
01912     mTextualContent += curNode->msgPart().bodyToUnicode();
01913     mTextualContentCharset = curNode->msgPart().charset();
01914     return true;
01915   }
01916 
01917   QByteArray decryptedBody;
01918   QString errorText;
01919   const QByteArray data = curNode->msgPart().bodyDecodedBinary();
01920   bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
01921   PartMetaData messagePart;
01922   messagePart.isDecryptable = bOkDecrypt;
01923   messagePart.isEncrypted = true;
01924   messagePart.isSigned = false;
01925   messagePart.errorText = errorText;
01926   if ( mReader )
01927     htmlWriter()->queue( writeSigstatHeader( messagePart,
01928                                              0, //cryptPlugWrapper(),
01929                                              curNode->trueFromAddress() ) );
01930   const QByteArray body = bOkDecrypt ? decryptedBody : data;
01931   const QString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
01932   const QTextCodec* aCodec = chiasmusCharset.isEmpty()
01933     ? codecFor( curNode )
01934     : KMMsgBase::codecForName( chiasmusCharset.ascii() );
01935   htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false /*decorate*/ ) );
01936   result.setInlineEncryptionState( KMMsgFullyEncrypted );
01937   if ( mReader )
01938     htmlWriter()->queue( writeSigstatFooter( messagePart ) );
01939   return true;
01940 }
01941 
01942 bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
01943 {
01944   Q_UNUSED( result );
01945   if ( !mReader )
01946     return false;
01947 
01948   const QString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
01949   KTNEFParser parser;
01950   if ( !parser.openFile( fileName ) || !parser.message()) {
01951     kdDebug() << k_funcinfo << "Could not parse " << fileName << endl;
01952     return false;
01953   }
01954 
01955   QPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
01956   if ( tnefatts.isEmpty() ) {
01957     kdDebug() << k_funcinfo << "No attachments found in " << fileName << endl;
01958     return false;
01959   }
01960 
01961   if ( !showOnlyOneMimePart() ) {
01962     QString label = node->msgPart().fileName().stripWhiteSpace();
01963     if ( label.isEmpty() )
01964       label = node->msgPart().name().stripWhiteSpace();
01965     label = KMMessage::quoteHtmlChars( label, true );
01966     const QString comment = KMMessage::quoteHtmlChars( node->msgPart().contentDescription(), true );
01967     const QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
01968 
01969     QString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
01970                 "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
01971     if ( !fileName.isEmpty() )
01972       htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
01973         + label + "</a>";
01974     else
01975       htmlStr += label;
01976     if ( !comment.isEmpty() )
01977       htmlStr += "<br>" + comment;
01978     htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
01979     htmlWriter()->queue( htmlStr );
01980   }
01981 
01982   for ( uint i = 0; i < tnefatts.count(); ++i ) {
01983     KTNEFAttach *att = tnefatts.at( i );
01984     QString label = att->displayName();
01985     if( label.isEmpty() )
01986       label = att->name();
01987     label = KMMessage::quoteHtmlChars( label, true );
01988 
01989     QString dir = mReader->createTempDir( "ktnef-" + QString::number( i ) );
01990     parser.extractFileTo( att->name(), dir );
01991     mReader->mTempFiles.append( dir + QDir::separator() + att->name() );
01992     QString href = "file:" + KURL::encode_string( dir + QDir::separator() + att->name() );
01993 
01994     KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
01995     QString iconName = KGlobal::instance()->iconLoader()->iconPath( mimeType->icon( QString(), false ), KIcon::Desktop );
01996 
01997     htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
01998                           iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
01999                           "</a></div><br>" );
02000   }
02001 
02002   if ( !showOnlyOneMimePart() )
02003     htmlWriter()->queue( "</td></tr></table>" );
02004 
02005   return true;
02006 }
02007 
02008   void ObjectTreeParser::writeBodyString( const QCString & bodyString,
02009                                           const QString & fromAddress,
02010                                           const QTextCodec * codec,
02011                                           ProcessResult & result,
02012                                           bool decorate ) {
02013     assert( mReader ); assert( codec );
02014     KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
02015     KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
02016     writeBodyStr( bodyString, codec, fromAddress,
02017                   inlineSignatureState, inlineEncryptionState, decorate );
02018     result.setInlineSignatureState( inlineSignatureState );
02019     result.setInlineEncryptionState( inlineEncryptionState );
02020   }
02021 
02022   void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
02023     if ( !mReader || !msgPart )
02024       return;
02025 
02026     QString label = msgPart->fileName();
02027     if( label.isEmpty() )
02028       label = msgPart->name();
02029     if( label.isEmpty() )
02030       label = "unnamed";
02031     label = KMMessage::quoteHtmlChars( label, true );
02032 
02033     QString comment = msgPart->contentDescription();
02034     comment = KMMessage::quoteHtmlChars( comment, true );
02035     if ( label == comment ) comment = QString::null;
02036 
02037     QString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
02038 
02039     QString href = QString( "attachment:%1?place=body" ).arg( partNum );
02040 
02041     QString iconName;
02042     if( inlineImage )
02043       iconName = href;
02044     else {
02045       iconName = msgPart->iconName();
02046       if( iconName.right( 14 ) == "mime_empty.png" ) {
02047         msgPart->magicSetType();
02048         iconName = msgPart->iconName();
02049       }
02050     }
02051 
02052     QCString contentId = msgPart->contentId();
02053     if ( !contentId.isEmpty() ) {
02054       htmlWriter()->embedPart( contentId, href );
02055     }
02056 
02057     if( inlineImage )
02058       // show the filename of the image below the embedded image
02059       htmlWriter()->queue( "<div><a href=\"" + href + "\">"
02060                            "<img src=\"" + fileName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
02061                            "</div>"
02062                            "<div><a href=\"" + href + "\">" + label + "</a>"
02063                            "</div>"
02064                            "<div>" + comment + "</div><br>" );
02065     else
02066       // show the filename next to the icon
02067       htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
02068                            iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
02069                            "</a></div>"
02070                            "<div>" + comment + "</div><br>" );
02071   }
02072 
02073 #define SIG_FRAME_COL_UNDEF  99
02074 #define SIG_FRAME_COL_RED    -1
02075 #define SIG_FRAME_COL_YELLOW  0
02076 #define SIG_FRAME_COL_GREEN   1
02077 QString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
02078                                         int status_code,
02079                                         GpgME::Signature::Summary summary,
02080                                         int& frameColor,
02081                                         bool& showKeyInfos )
02082 {
02083     // note: At the moment frameColor and showKeyInfos are
02084     //       used for CMS only but not for PGP signatures
02085     // pending(khz): Implement usage of these for PGP sigs as well.
02086     showKeyInfos = true;
02087     QString result;
02088     if( cryptProto ) {
02089         if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
02090             // process enum according to it's definition to be read in
02091             // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
02092             switch( status_code ) {
02093             case 0: // GPGME_SIG_STAT_NONE
02094                 result = i18n("Error: Signature not verified");
02095                 break;
02096             case 1: // GPGME_SIG_STAT_GOOD
02097                 result = i18n("Good signature");
02098                 break;
02099             case 2: // GPGME_SIG_STAT_BAD
02100                 result = i18n("<b>Bad</b> signature");
02101                 break;
02102             case 3: // GPGME_SIG_STAT_NOKEY
02103                 result = i18n("No public key to verify the signature");
02104                 break;
02105             case 4: // GPGME_SIG_STAT_NOSIG
02106                 result = i18n("No signature found");
02107                 break;
02108             case 5: // GPGME_SIG_STAT_ERROR
02109                 result = i18n("Error verifying the signature");
02110                 break;
02111             case 6: // GPGME_SIG_STAT_DIFF
02112                 result = i18n("Different results for signatures");
02113                 break;
02114             /* PENDING(khz) Verify exact meaning of the following values:
02115             case 7: // GPGME_SIG_STAT_GOOD_EXP
02116                 return i18n("Signature certificate is expired");
02117             break;
02118             case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
02119                 return i18n("One of the certificate's keys is expired");
02120             break;
02121             */
02122             default:
02123                 result = "";   // do *not* return a default text here !
02124                 break;
02125             }
02126         }
02127         else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
02128             // process status bits according to SigStatus_...
02129             // definitions in kdenetwork/libkdenetwork/cryptplug.h
02130 
02131             if( summary == GpgME::Signature::None ) {
02132                 result = i18n("No status information available.");
02133                 frameColor = SIG_FRAME_COL_YELLOW;
02134                 showKeyInfos = false;
02135                 return result;
02136             }
02137 
02138             if( summary & GpgME::Signature::Valid ) {
02139                 result = i18n("Good signature.");
02140                 // Note:
02141                 // Here we are work differently than KMail did before!
02142                 //
02143                 // The GOOD case ( == sig matching and the complete
02144                 // certificate chain was verified and is valid today )
02145                 // by definition does *not* show any key
02146                 // information but just states that things are OK.
02147                 //           (khz, according to LinuxTag 2002 meeting)
02148                 frameColor = SIG_FRAME_COL_GREEN;
02149                 showKeyInfos = false;
02150                 return result;
02151             }
02152 
02153             // we are still there?  OK, let's test the different cases:
02154 
02155             // we assume green, test for yellow or red (in this order!)
02156             frameColor = SIG_FRAME_COL_GREEN;
02157             QString result2;
02158             if( summary & GpgME::Signature::KeyExpired ){
02159                 // still is green!
02160                 result2 += i18n("One key has expired.");
02161             }
02162             if( summary & GpgME::Signature::SigExpired ){
02163                 // and still is green!
02164                 result2 += i18n("The signature has expired.");
02165             }
02166 
02167             // test for yellow:
02168             if( summary & GpgME::Signature::KeyMissing ) {
02169                 result2 += i18n("Unable to verify: key missing.");
02170                 // if the signature certificate is missing
02171                 // we cannot show infos on it
02172                 showKeyInfos = false;
02173                 frameColor = SIG_FRAME_COL_YELLOW;
02174             }
02175             if( summary & GpgME::Signature::CrlMissing ){
02176                 result2 += i18n("CRL not available.");
02177                 frameColor = SIG_FRAME_COL_YELLOW;
02178             }
02179             if( summary & GpgME::Signature::CrlTooOld ){
02180                 result2 += i18n("Available CRL is too old.");
02181                 frameColor = SIG_FRAME_COL_YELLOW;
02182             }
02183             if( summary & GpgME::Signature::BadPolicy ){
02184                 result2 += i18n("A policy was not met.");
02185                 frameColor = SIG_FRAME_COL_YELLOW;
02186             }
02187             if( summary & GpgME::Signature::SysError ){
02188                 result2 += i18n("A system error occurred.");
02189                 // if a system error occurred
02190                 // we cannot trust any information
02191                 // that was given back by the plug-in
02192                 showKeyInfos = false;
02193                 frameColor = SIG_FRAME_COL_YELLOW;
02194             }
02195 
02196             // test for red:
02197             if( summary & GpgME::Signature::KeyRevoked ){
02198                 // this is red!
02199                 result2 += i18n("One key has been revoked.");
02200                 frameColor = SIG_FRAME_COL_RED;
02201             }
02202             if( summary & GpgME::Signature::Red ) {
02203                 if( result2.isEmpty() )
02204                     // Note:
02205                     // Here we are work differently than KMail did before!
02206                     //
02207                     // The BAD case ( == sig *not* matching )
02208                     // by definition does *not* show any key
02209                     // information but just states that things are BAD.
02210                     //
02211                     // The reason for this: In this case ALL information
02212                     // might be falsificated, we can NOT trust the data
02213                     // in the body NOT the signature - so we don't show
02214                     // any key/signature information at all!
02215                     //         (khz, according to LinuxTag 2002 meeting)
02216                     showKeyInfos = false;
02217                 frameColor = SIG_FRAME_COL_RED;
02218             }
02219             else
02220                 result = "";
02221 
02222             if( SIG_FRAME_COL_GREEN == frameColor ) {
02223                 result = i18n("Good signature.");
02224             } else if( SIG_FRAME_COL_RED == frameColor ) {
02225                 result = i18n("<b>Bad</b> signature.");
02226             } else
02227                 result = "";
02228 
02229             if( !result2.isEmpty() ) {
02230                 if( !result.isEmpty() )
02231                     result.append("<br />");
02232                 result.append( result2 );
02233             }
02234         }
02235         /*
02236         // add i18n support for 3rd party plug-ins here:
02237         else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
02238 
02239         }
02240         */
02241     }
02242     return result;
02243 }
02244 
02245 
02246 static QString writeSimpleSigstatHeader( const PartMetaData &block )
02247 {
02248   QString html;
02249   html += "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
02250 
02251   if ( block.signClass == "signErr" ) {
02252     html += i18n( "Invalid signature." );
02253   } else if ( block.signClass == "signOkKeyBad" || block.signClass == "signWarn" ) {
02254     html += i18n( "Not enough information to check signature validity." );
02255   } else if ( block.signClass == "signOkKeyOk" ) {
02256     QString addr;
02257     if ( !block.signerMailAddresses.isEmpty() )
02258       addr = block.signerMailAddresses.first();
02259     QString name = addr;
02260     if ( name.isEmpty() )
02261       name = block.signer;
02262     if ( addr.isEmpty() ) {
02263       html += i18n( "Signature is valid." );
02264     } else {
02265       html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
02266     }
02267   } else {
02268     // should not happen
02269     html += i18n( "Unknown signature state" );
02270   }
02271   html += "</td><td align=\"right\">";
02272   html += "<a href=\"kmail:showSignatureDetails\">";
02273   html += i18n( "Show Details" );
02274   html += "</a></td></tr></table>";
02275   return html;
02276 }
02277 
02278 static QString beginVerboseSigstatHeader()
02279 {
02280   return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
02281 }
02282 
02283 static QString makeShowAuditLogLink( const GpgME::Error & err, const QString & auditLog ) {
02284   if ( const unsigned int code = err.code() ) {
02285     if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
02286       //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not implemented)" << endl;
02287       return QString();
02288     } else if ( code == GPG_ERR_NO_DATA ) {
02289       //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not available)" << endl;
02290       return i18n("No Audit Log available");
02291     } else {
02292       return i18n("Error Retrieving Audit Log: %1").arg( QString::fromLocal8Bit( err.asString() ) );
02293     }
02294   }
02295 
02296   if ( !auditLog.isEmpty() ) {
02297     KURL url;
02298     url.setProtocol( "kmail" );
02299     url.setPath( "showAuditLog" );
02300     url.addQueryItem( "log", auditLog );
02301 
02302     return "<a href=\"" + url.htmlURL() + "\">" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + "</a>";
02303   }
02304 
02305   return QString::null;
02306 }
02307 
02308 static QString endVerboseSigstatHeader( const PartMetaData & pmd )
02309 {
02310   QString html;
02311   html += "</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
02312   html += "<a href=\"kmail:hideSignatureDetails\">";
02313   html += i18n( "Hide Details" );
02314   html += "</a></td></tr>";
02315   html += "<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
02316   html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
02317   html += "</td></tr></table>";
02318   return html;
02319 }
02320 
02321 QString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
02322                                               const Kleo::CryptoBackend::Protocol * cryptProto,
02323                                               const QString & fromAddress,
02324                                               partNode *node )
02325 {
02326     const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
02327     QString signer = block.signer;
02328 
02329     QString htmlStr, simpleHtmlStr;
02330     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02331     QString cellPadding("cellpadding=\"1\"");
02332 
02333     if( block.isEncapsulatedRfc822Message )
02334     {
02335         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
02336             "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
02337         if ( node )
02338           htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
02339                      + i18n("Encapsulated message") + "</a>";
02340         else
02341           htmlStr += i18n("Encapsulated message");
02342         htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
02343     }
02344 
02345     if( block.isEncrypted )
02346     {
02347         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
02348             "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
02349         if ( block.inProgress )
02350             htmlStr += i18n("Please wait while the message is being decrypted...");
02351         else if ( block.isDecryptable )
02352             htmlStr += i18n("Encrypted message");
02353         else {
02354             htmlStr += i18n("Encrypted message (decryption not possible)");
02355             if( !block.errorText.isEmpty() )
02356                 htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
02357         }
02358         htmlStr += "</td></tr><tr class=\"encrB\"><td>";
02359     }
02360 
02361     if ( block.isSigned && block.inProgress )
02362     {
02363         block.signClass = "signInProgress";
02364         htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"signInProgress\">"
02365             "<tr class=\"signInProgressH\"><td dir=\"" + dir + "\">";
02366         htmlStr += i18n("Please wait while the signature is being verified...");
02367         htmlStr += "</td></tr><tr class=\"signInProgressB\"><td>";
02368     }
02369     simpleHtmlStr = htmlStr;
02370 
02371     if ( block.isSigned && !block.inProgress ) {
02372         QStringList& blockAddrs( block.signerMailAddresses );
02373         // note: At the moment frameColor and showKeyInfos are
02374         //       used for CMS only but not for PGP signatures
02375         // pending(khz): Implement usage of these for PGP sigs as well.
02376         int frameColor = SIG_FRAME_COL_UNDEF;
02377         bool showKeyInfos;
02378         bool onlyShowKeyURL = false;
02379         bool cannotCheckSignature = true;
02380         QString statusStr = sigStatusToString( cryptProto,
02381                                                block.status_code,
02382                                                block.sigSummary,
02383                                                frameColor,
02384                                                showKeyInfos );
02385         // if needed fallback to english status text
02386         // that was reported by the plugin
02387         if( statusStr.isEmpty() )
02388             statusStr = block.status;
02389         if( block.technicalProblem )
02390             frameColor = SIG_FRAME_COL_YELLOW;
02391 
02392         switch( frameColor ){
02393             case SIG_FRAME_COL_RED:
02394                 cannotCheckSignature = false;
02395                 break;
02396             case SIG_FRAME_COL_YELLOW:
02397                 cannotCheckSignature = true;
02398                 break;
02399             case SIG_FRAME_COL_GREEN:
02400                 cannotCheckSignature = false;
02401                 break;
02402         }
02403 
02404         // compose the string for displaying the key ID
02405         // either as URL or not linked (for PGP)
02406         // note: Once we can start PGP key manager programs
02407         //       from within KMail we could change this and
02408         //       always show the URL.    (khz, 2002/06/27)
02409         QString startKeyHREF;
02410         if( isSMIME )
02411             startKeyHREF =
02412                 QString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
02413                 .arg( cryptProto->displayName(),
02414                       cryptProto->name(),
02415                       block.keyId );
02416         QString keyWithWithoutURL
02417             = isSMIME
02418             ? QString("%1%2</a>")
02419                 .arg( startKeyHREF,
02420                       cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
02421             : "0x" + QString::fromUtf8( block.keyId );
02422 
02423 
02424         // temporary hack: always show key infos!
02425         showKeyInfos = true;
02426 
02427         // Sorry for using 'black' as null color but .isValid()
02428         // checking with QColor default c'tor did not work for
02429         // some reason.
02430         if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
02431 
02432             // new frame settings for CMS:
02433             // beautify the status string
02434             if( !statusStr.isEmpty() ) {
02435                 statusStr.prepend("<i>");
02436                 statusStr.append( "</i>");
02437             }
02438 
02439             // special color handling: S/MIME uses only green/yellow/red.
02440             switch( frameColor ) {
02441                 case SIG_FRAME_COL_RED:
02442                     block.signClass = "signErr";//"signCMSRed";
02443                     onlyShowKeyURL = true;
02444                     break;
02445                 case SIG_FRAME_COL_YELLOW:
02446                     if( block.technicalProblem )
02447                         block.signClass = "signWarn";
02448                     else
02449                         block.signClass = "signOkKeyBad";//"signCMSYellow";
02450                     break;
02451                 case SIG_FRAME_COL_GREEN:
02452                     block.signClass = "signOkKeyOk";//"signCMSGreen";
02453                     // extra hint for green case
02454                     // that email addresses in DN do not match fromAddress
02455                     QString greenCaseWarning;
02456                     QString msgFrom( KPIM::getEmailAddress(fromAddress) );
02457                     QString certificate;
02458                     if( block.keyId.isEmpty() )
02459                         certificate = i18n("certificate");
02460                     else
02461                         certificate = startKeyHREF + i18n("certificate") + "</a>";
02462                     if( !blockAddrs.empty() ){
02463                         if( blockAddrs.grep(
02464                                 msgFrom,
02465                                 false ).isEmpty() ) {
02466                             greenCaseWarning =
02467                                 "<u>" +
02468                                 i18n("Warning:") +
02469                                 "</u> " +
02470                                 i18n("Sender's mail address is not stored "
02471                                      "in the %1 used for signing.").arg(certificate) +
02472                                 "<br />" +
02473                                 i18n("sender: ") +
02474                                 msgFrom +
02475                                 "<br />" +
02476                                 i18n("stored: ");
02477                             // We cannot use Qt's join() function here but
02478                             // have to join the addresses manually to
02479                             // extract the mail addresses (without '<''>')
02480                             // before including it into our string:
02481                             bool bStart = true;
02482                             for(QStringList::ConstIterator it = blockAddrs.begin();
02483                                 it != blockAddrs.end(); ++it ){
02484                                 if( !bStart )
02485                                     greenCaseWarning.append(", <br />&nbsp; &nbsp;");
02486                                 bStart = false;
02487                                 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
02488                             }
02489                         }
02490                     } else {
02491                         greenCaseWarning =
02492                             "<u>" +
02493                             i18n("Warning:") +
02494                             "</u> " +
02495                             i18n("No mail address is stored in the %1 used for signing, "
02496                                  "so we cannot compare it to the sender's address %2.")
02497                             .arg(certificate,msgFrom);
02498                     }
02499                     if( !greenCaseWarning.isEmpty() ) {
02500                         if( !statusStr.isEmpty() )
02501                             statusStr.append("<br />&nbsp;<br />");
02502                         statusStr.append( greenCaseWarning );
02503                     }
02504                     break;
02505             }
02506 
02507             QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02508                 "class=\"" + block.signClass + "\">"
02509                 "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02510             htmlStr += frame + beginVerboseSigstatHeader();
02511             simpleHtmlStr += frame;
02512             simpleHtmlStr += writeSimpleSigstatHeader( block );
02513             if( block.technicalProblem ) {
02514                 htmlStr += block.errorText;
02515             }
02516             else if( showKeyInfos ) {
02517                 if( cannotCheckSignature ) {
02518                     htmlStr += i18n( "Not enough information to check "
02519                                      "signature. %1" )
02520                                 .arg( keyWithWithoutURL );
02521                 }
02522                 else {
02523 
02524                     if (block.signer.isEmpty())
02525                         signer = "";
02526                     else {
02527                         if( !blockAddrs.empty() ){
02528                             QString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
02529                             signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
02530                         }
02531                     }
02532 
02533                     if( block.keyId.isEmpty() ) {
02534                         if( signer.isEmpty() || onlyShowKeyURL )
02535                             htmlStr += i18n( "Message was signed with unknown key." );
02536                         else
02537                             htmlStr += i18n( "Message was signed by %1." )
02538                                     .arg( signer );
02539                     } else {
02540                         QDateTime created = block.creationTime;
02541                         if( created.isValid() ) {
02542                             if( signer.isEmpty() ) {
02543                                 if( onlyShowKeyURL )
02544                                     htmlStr += i18n( "Message was signed with key %1." )
02545                                                 .arg( keyWithWithoutURL );
02546                                 else
02547                                     htmlStr += i18n( "Message was signed on %1 with key %2." )
02548                                                 .arg( KGlobal::locale()->formatDateTime( created ),
02549                                                       keyWithWithoutURL );
02550                             }
02551                             else {
02552                                 if( onlyShowKeyURL )
02553                                     htmlStr += i18n( "Message was signed with key %1." )
02554                                             .arg( keyWithWithoutURL );
02555                                 else
02556                                     htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
02557                                             .arg( KGlobal::locale()->formatDateTime( created ),
02558                                                   keyWithWithoutURL,
02559                                                   signer );
02560                             }
02561                         }
02562                         else {
02563                             if( signer.isEmpty() || onlyShowKeyURL )
02564                                 htmlStr += i18n( "Message was signed with key %1." )
02565                                         .arg( keyWithWithoutURL );
02566                             else
02567                                 htmlStr += i18n( "Message was signed by %2 with key %1." )
02568                                         .arg( keyWithWithoutURL,
02569                                               signer );
02570                         }
02571                     }
02572                 }
02573                 htmlStr += "<br />";
02574                 if( !statusStr.isEmpty() ) {
02575                     htmlStr += "&nbsp;<br />";
02576                     htmlStr += i18n( "Status: " );
02577                     htmlStr += statusStr;
02578                 }
02579             } else {
02580                 htmlStr += statusStr;
02581             }
02582             frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02583             htmlStr += endVerboseSigstatHeader( block ) + frame;
02584             simpleHtmlStr += frame;
02585 
02586         } else {
02587 
02588             // old frame settings for PGP:
02589 
02590             if( block.signer.isEmpty() || block.technicalProblem ) {
02591                 block.signClass = "signWarn";
02592                 QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02593                     "class=\"" + block.signClass + "\">"
02594                     "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02595                 htmlStr += frame + beginVerboseSigstatHeader();
02596                 simpleHtmlStr += frame;
02597                 simpleHtmlStr += writeSimpleSigstatHeader( block );
02598                 if( block.technicalProblem ) {
02599                     htmlStr += block.errorText;
02600                 }
02601                 else {
02602                   if( !block.keyId.isEmpty() ) {
02603                     QDateTime created = block.creationTime;
02604                     if ( created.isValid() )
02605                         htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
02606                                 .arg( KGlobal::locale()->formatDateTime( created ),
02607                                       keyWithWithoutURL );
02608                     else
02609                         htmlStr += i18n( "Message was signed with unknown key %1." )
02610                                 .arg( keyWithWithoutURL );
02611                   }
02612                   else
02613                     htmlStr += i18n( "Message was signed with unknown key." );
02614                   htmlStr += "<br />";
02615                   htmlStr += i18n( "The validity of the signature cannot be "
02616                                    "verified." );
02617                   if( !statusStr.isEmpty() ) {
02618                     htmlStr += "<br />";
02619                     htmlStr += i18n( "Status: " );
02620                     htmlStr += "<i>";
02621                     htmlStr += statusStr;
02622                     htmlStr += "</i>";
02623                   }
02624                 }
02625                 frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
02626                 htmlStr += endVerboseSigstatHeader( block ) + frame;
02627                 simpleHtmlStr += frame;
02628             }
02629             else
02630             {
02631                 // HTMLize the signer's user id and create mailto: link
02632                 signer = KMMessage::quoteHtmlChars( signer, true );
02633                 signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
02634 
02635                 if (block.isGoodSignature) {
02636                     if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
02637                         block.signClass = "signOkKeyBad";
02638                     else
02639                         block.signClass = "signOkKeyOk";
02640                     QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02641                         "class=\"" + block.signClass + "\">"
02642                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02643                     htmlStr += frame + beginVerboseSigstatHeader();
02644                     simpleHtmlStr += frame;
02645                     simpleHtmlStr += writeSimpleSigstatHeader( block );
02646                     if( !block.keyId.isEmpty() )
02647                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02648                                    .arg( keyWithWithoutURL,
02649                                          signer );
02650                     else
02651                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02652                     htmlStr += "<br />";
02653 
02654                     switch( block.keyTrust )
02655                     {
02656                         case Kpgp::KPGP_VALIDITY_UNKNOWN:
02657                         htmlStr += i18n( "The signature is valid, but the key's "
02658                                 "validity is unknown." );
02659                         break;
02660                         case Kpgp::KPGP_VALIDITY_MARGINAL:
02661                         htmlStr += i18n( "The signature is valid and the key is "
02662                                 "marginally trusted." );
02663                         break;
02664                         case Kpgp::KPGP_VALIDITY_FULL:
02665                         htmlStr += i18n( "The signature is valid and the key is "
02666                                 "fully trusted." );
02667                         break;
02668                         case Kpgp::KPGP_VALIDITY_ULTIMATE:
02669                         htmlStr += i18n( "The signature is valid and the key is "
02670                                 "ultimately trusted." );
02671                         break;
02672                         default:
02673                         htmlStr += i18n( "The signature is valid, but the key is "
02674                                 "untrusted." );
02675                     }
02676                     frame = "</td></tr>"
02677                         "<tr class=\"" + block.signClass + "B\"><td>";
02678                     htmlStr += endVerboseSigstatHeader( block ) + frame;
02679                     simpleHtmlStr += frame;
02680                 }
02681                 else
02682                 {
02683                     block.signClass = "signErr";
02684                     QString frame = "<table cellspacing=\"1\" "+cellPadding+" "
02685                         "class=\"" + block.signClass + "\">"
02686                         "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
02687                     htmlStr += frame + beginVerboseSigstatHeader();
02688                     simpleHtmlStr += frame;
02689                     simpleHtmlStr += writeSimpleSigstatHeader( block );
02690                     if( !block.keyId.isEmpty() )
02691                         htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
02692                         .arg( keyWithWithoutURL,
02693                               signer );
02694                     else
02695                         htmlStr += i18n( "Message was signed by %1." ).arg( signer );
02696                     htmlStr += "<br />";
02697                     htmlStr += i18n("Warning: The signature is bad.");
02698                     frame = "</td></tr>"
02699                         "<tr class=\"" + block.signClass + "B\"><td>";
02700                     htmlStr += endVerboseSigstatHeader( block ) + frame;
02701                     simpleHtmlStr += frame;
02702                 }
02703             }
02704         }
02705     }
02706 
02707     if ( mReader->showSignatureDetails() )
02708       return htmlStr;
02709     return simpleHtmlStr;
02710 }
02711 
02712 QString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
02713 {
02714     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02715 
02716     QString htmlStr;
02717 
02718     if (block.isSigned) {
02719         htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
02720         htmlStr += "<td dir=\"" + dir + "\">" +
02721             i18n( "End of signed message" ) +
02722             "</td></tr></table>";
02723     }
02724 
02725     if (block.isEncrypted) {
02726         htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
02727                 i18n( "End of encrypted message" ) +
02728             "</td></tr></table>";
02729     }
02730 
02731     if( block.isEncapsulatedRfc822Message )
02732     {
02733         htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
02734             i18n( "End of encapsulated message" ) +
02735             "</td></tr></table>";
02736     }
02737 
02738     return htmlStr;
02739 }
02740 
02741 //-----------------------------------------------------------------------------
02742 
02743 void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
02744 {
02745   if ( !mReader )
02746     return;
02747 
02748   htmlWriter()->queue( QString( "<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
02749 }
02750 
02751 //-----------------------------------------------------------------------------
02752 
02753 void ObjectTreeParser::writeAttachmentMarkFooter()
02754 {
02755   if ( !mReader )
02756     return;
02757 
02758   htmlWriter()->queue( QString( "</div>" ) );
02759 }
02760 
02761 //-----------------------------------------------------------------------------
02762 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02763                                 const QString& fromAddress )
02764 {
02765   KMMsgSignatureState dummy1;
02766   KMMsgEncryptionState dummy2;
02767   writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
02768 }
02769 
02770 //-----------------------------------------------------------------------------
02771 void ObjectTreeParser::writeBodyStr( const QCString& aStr, const QTextCodec *aCodec,
02772                                 const QString& fromAddress,
02773                                 KMMsgSignatureState&  inlineSignatureState,
02774                                 KMMsgEncryptionState& inlineEncryptionState,
02775                                 bool decorate )
02776 {
02777   bool goodSignature = false;
02778   Kpgp::Module* pgp = Kpgp::Module::getKpgp();
02779   assert(pgp != 0);
02780   bool isPgpMessage = false; // true if the message contains at least one
02781                              // PGP MESSAGE or one PGP SIGNED MESSAGE block
02782   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
02783   QString headerStr = QString("<div dir=\"%1\">").arg(dir);
02784 
02785   inlineSignatureState  = KMMsgNotSigned;
02786   inlineEncryptionState = KMMsgNotEncrypted;
02787   QPtrList<Kpgp::Block> pgpBlocks;
02788   QStrList nonPgpBlocks;
02789   if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
02790   {
02791       bool isEncrypted = false, isSigned = false;
02792       bool fullySignedOrEncrypted = true;
02793       bool firstNonPgpBlock = true;
02794       bool couldDecrypt = false;
02795       QString signer;
02796       QCString keyId;
02797       QString decryptionError;
02798       Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
02799 
02800       QPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
02801 
02802       QStrListIterator npbit( nonPgpBlocks );
02803 
02804       QString htmlStr;
02805       for( ; *pbit != 0; ++pbit, ++npbit )
02806       {
02807           // insert the next Non-OpenPGP block
02808           QCString str( *npbit );
02809           if( !str.isEmpty() ) {
02810             htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02811             //kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
02812             //                << "'" << endl;
02813             // treat messages with empty lines before the first clearsigned
02814             // block as fully signed/encrypted
02815             if( firstNonPgpBlock ) {
02816               // check whether str only consists of \n
02817               for( QCString::ConstIterator c = str.begin(); *c; ++c ) {
02818                 if( *c != '\n' ) {
02819                   fullySignedOrEncrypted = false;
02820                   break;
02821                 }
02822               }
02823             }
02824             else {
02825               fullySignedOrEncrypted = false;
02826             }
02827           }
02828           firstNonPgpBlock = false;
02829 
02830           //htmlStr += "<br>";
02831 
02832           Kpgp::Block* block = *pbit;
02833           if( ( block->type() == Kpgp::PgpMessageBlock &&
02834                 // ### Workaround for bug 56693
02835                 !kmkernel->contextMenuShown() ) ||
02836               ( block->type() == Kpgp::ClearsignedBlock ) )
02837           {
02838               isPgpMessage = true;
02839               if( block->type() == Kpgp::PgpMessageBlock )
02840               {
02841                 if ( mReader )
02842                   emit mReader->noDrag();
02843                 // try to decrypt this OpenPGP block
02844                 couldDecrypt = block->decrypt();
02845                 isEncrypted = block->isEncrypted();
02846                 if (!couldDecrypt) {
02847                   decryptionError = pgp->lastErrorMsg();
02848                 }
02849               }
02850               else
02851               {
02852                   // try to verify this OpenPGP block
02853                   block->verify();
02854               }
02855 
02856               isSigned = block->isSigned();
02857               if( isSigned )
02858               {
02859                   keyId = block->signatureKeyId();
02860                   signer = block->signatureUserId();
02861                   if( !signer.isEmpty() )
02862                   {
02863                       goodSignature = block->goodSignature();
02864 
02865                       if( !keyId.isEmpty() ) {
02866                         keyTrust = pgp->keyTrust( keyId );
02867                         Kpgp::Key* key = pgp->publicKey( keyId );
02868                         if ( key ) {
02869                           // Use the user ID from the key because this one
02870                           // is charset safe.
02871                           signer = key->primaryUserID();
02872                         }
02873                       }
02874                       else
02875                         // This is needed for the PGP 6 support because PGP 6 doesn't
02876                         // print the key id of the signing key if the key is known.
02877                         keyTrust = pgp->keyTrust( signer );
02878                   }
02879               }
02880 
02881               if( isSigned )
02882                 inlineSignatureState = KMMsgPartiallySigned;
02883               if( isEncrypted )
02884                 inlineEncryptionState = KMMsgPartiallyEncrypted;
02885 
02886               PartMetaData messagePart;
02887 
02888               messagePart.isSigned = isSigned;
02889               messagePart.technicalProblem = false;
02890               messagePart.isGoodSignature = goodSignature;
02891               messagePart.isEncrypted = isEncrypted;
02892               messagePart.isDecryptable = couldDecrypt;
02893               messagePart.decryptionError = decryptionError;
02894               messagePart.signer = signer;
02895               messagePart.keyId = keyId;
02896               messagePart.keyTrust = keyTrust;
02897 
02898               htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
02899 
02900               htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
02901               htmlStr += writeSigstatFooter( messagePart );
02902           }
02903           else // block is neither message block nor clearsigned block
02904             htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
02905                                    decorate );
02906       }
02907 
02908       // add the last Non-OpenPGP block
02909       QCString str( nonPgpBlocks.last() );
02910       if( !str.isEmpty() ) {
02911         htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
02912         // Even if the trailing Non-OpenPGP block isn't empty we still
02913         // consider the message part fully signed/encrypted because else
02914         // all inline signed mailing list messages would only be partially
02915         // signed because of the footer which is often added by the mailing
02916         // list software. IK, 2003-02-15
02917       }
02918       if( fullySignedOrEncrypted ) {
02919         if( inlineSignatureState == KMMsgPartiallySigned )
02920           inlineSignatureState = KMMsgFullySigned;
02921         if( inlineEncryptionState == KMMsgPartiallyEncrypted )
02922           inlineEncryptionState = KMMsgFullyEncrypted;
02923       }
02924       htmlWriter()->queue( htmlStr );
02925   }
02926   else
02927     htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
02928 }
02929 
02930 
02931 QString ObjectTreeParser::quotedHTML( const QString& s, bool decorate )
02932 {
02933   assert( mReader );
02934   assert( cssHelper() );
02935 
02936   int convertFlags = LinkLocator::PreserveSpaces;
02937   if ( decorate && GlobalSettings::self()->showEmoticons() ) {
02938     convertFlags |= LinkLocator::ReplaceSmileys;
02939   }
02940   QString htmlStr;
02941   const QString normalStartTag = cssHelper()->nonQuotedFontTag();
02942   QString quoteFontTag[3];
02943   QString deepQuoteFontTag[3];
02944   for ( int i = 0 ; i < 3 ; ++i ) {
02945     quoteFontTag[i] = cssHelper()->quoteFontTag( i );
02946     deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
02947   }
02948   const QString normalEndTag = "</div>";
02949   const QString quoteEnd = "</div>";
02950 
02951   unsigned int pos, beg;
02952   const unsigned int length = s.length();
02953 
02954   // skip leading empty lines
02955   for ( pos = 0; pos < length && s[pos] <= ' '; pos++ ) { ; }
02956   while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
02957   beg = pos;
02958 
02959   int currQuoteLevel = -2; // -2 == no previous lines
02960   bool curHidden = false; // no hide any block
02961 
02962   while (beg<length)
02963   {
02964     QString line;
02965 
02966     /* search next occurrence of '\n' */
02967     pos = s.find('\n', beg, FALSE);
02968     if (pos == (unsigned int)(-1))
02969         pos = length;
02970 
02971     line = s.mid(beg,pos-beg);
02972     beg = pos+1;
02973 
02974     /* calculate line's current quoting depth */
02975     int actQuoteLevel = -1;
02976 
02977     if ( GlobalSettings::self()->showExpandQuotesMark() )
02978     {
02979       // Cache Icons
02980       if ( mCollapseIcon.isEmpty() ) {
02981         mCollapseIcon= LinkLocator::pngToDataUrl(
02982             KGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
02983       }
02984       if ( mExpandIcon.isEmpty() )
02985         mExpandIcon= LinkLocator::pngToDataUrl(
02986             KGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
02987     }
02988 
02989     for (unsigned int p=0; p<line.length(); p++) {
02990       switch (line[p].latin1()) {
02991         case '>':
02992         case '|':
02993           actQuoteLevel++;
02994           break;
02995         case ' ':  // spaces and tabs are allowed between the quote markers
02996         case '\t':
02997         case '\r':
02998           break;
02999         default:  // stop quoting depth calculation
03000           p = line.length();
03001           break;
03002       }
03003     } /* for() */
03004 
03005     bool actHidden = false;
03006     QString textExpand;
03007 
03008     // This quoted line needs be hiden
03009     if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
03010         && mReader->mLevelQuote <= ( actQuoteLevel ) )
03011       actHidden = true;
03012 
03013     if ( actQuoteLevel != currQuoteLevel ) {
03014       /* finish last quotelevel */
03015       if (currQuoteLevel == -1)
03016         htmlStr.append( normalEndTag );
03017       else if ( currQuoteLevel >= 0 && !curHidden )
03018         htmlStr.append( quoteEnd );
03019 
03020       /* start new quotelevel */
03021       if (actQuoteLevel == -1)
03022         htmlStr += normalStartTag;
03023       else
03024       {
03025         if ( GlobalSettings::self()->showExpandQuotesMark() )
03026         {
03027           if (  actHidden )
03028           {
03029             //only show the QuoteMark when is the first line of the level hidden
03030             if ( !curHidden )
03031             {
03032               //Expand all quotes
03033               htmlStr += "<div class=\"quotelevelmark\" >" ;
03034               htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
03035                   "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03036                 .arg(-1)
03037                 .arg( mExpandIcon );
03038               htmlStr += "</div><br/>";
03039               htmlStr += quoteEnd;
03040             }
03041           }else {
03042             htmlStr += "<div class=\"quotelevelmark\" >" ;
03043             htmlStr += QString( "<a href=\"kmail:levelquote?%1 \">"
03044                 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
03045               .arg(actQuoteLevel)
03046               .arg( mCollapseIcon);
03047             htmlStr += "</div>";
03048             if ( actQuoteLevel < 3 )
03049               htmlStr += quoteFontTag[actQuoteLevel];
03050             else
03051               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03052           }
03053         } else
03054             if ( actQuoteLevel < 3 )
03055               htmlStr += quoteFontTag[actQuoteLevel];
03056             else
03057               htmlStr += deepQuoteFontTag[actQuoteLevel%3];
03058       }
03059       currQuoteLevel = actQuoteLevel;
03060     }
03061     curHidden = actHidden;
03062 
03063 
03064     if ( !actHidden )
03065     {
03066       // don't write empty <div ...></div> blocks (they have zero height)
03067       // ignore ^M DOS linebreaks
03068       if( !line.replace('\015', "").isEmpty() )
03069       {
03070          htmlStr +=QString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
03071          htmlStr += LinkLocator::convertToHtml( line, convertFlags );
03072          htmlStr += QString( "</div>" );
03073       }
03074       else
03075         htmlStr += "<br>";
03076     }
03077   } /* while() */
03078 
03079   /* really finish the last quotelevel */
03080   if (currQuoteLevel == -1)
03081      htmlStr.append( normalEndTag );
03082   else
03083      htmlStr.append( quoteEnd );
03084 
03085   return htmlStr;
03086 }
03087 
03088 
03089 
03090   const QTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
03091     assert( node );
03092     if ( mReader && mReader->overrideCodec() )
03093       return mReader->overrideCodec();
03094     return node->msgPart().codec();
03095   }
03096 
03097 #ifdef MARCS_DEBUG
03098   void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
03099                                      size_t len ) {
03100     assert( filename );
03101 
03102     QFile f( filename );
03103     if ( f.open( IO_WriteOnly ) ) {
03104       if ( start ) {
03105         QDataStream ds( &f );
03106         ds.writeRawBytes( start, len );
03107       }
03108       f.close();  // If data is 0 we just create a zero length file.
03109     }
03110   }
03111 #endif // !NDEBUG
03112 
03113 
03114 } // namespace KMail
KDE Home | KDE Accessibility Home | Description of Access Keys