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