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