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