kmail

objecttreeparser.cpp

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