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