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