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