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