kmail Library API Documentation

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