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