kmail

objecttreeparser.cpp

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