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