kmail

partNode.cpp
00001 /* -*- c++ -*-
00002     partNode.cpp A node in a MIME tree.
00003 
00004     This file is part of KMail, the KDE mail client.
00005     Copyright (c) 2002 Klarälvdalens Datakonsult AB
00006 
00007     KMail is free software; you can redistribute it and/or modify it
00008     under the terms of the GNU General Public License, version 2, as
00009     published by the Free Software Foundation.
00010 
00011     KMail is distributed in the hope that it will be useful, but
00012     WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00019 
00020     In addition, as a special exception, the copyright holders give
00021     permission to link the code of this program with any edition of
00022     the TQt library by Trolltech AS, Norway (or with modified versions
00023     of TQt that use the same license as TQt), and distribute linked
00024     combinations including the two.  You must obey the GNU General
00025     Public License in all respects for all of the code used other than
00026     TQt.  If you modify this file, you may extend this exception to
00027     your version of the file, but you are not obligated to do so.  If
00028     you do not wish to do so, delete this exception statement from
00029     your version.
00030 */
00031 
00032 #include <config.h>
00033 
00034 #include "partNode.h"
00035 #include "kmreaderwin.h"
00036 
00037 #include <tdelocale.h>
00038 #include <kdebug.h>
00039 #include "kmmimeparttree.h"
00040 #include <mimelib/utility.h>
00041 #include <tqregexp.h>
00042 #include <kasciistricmp.h>
00043 #include "util.h"
00044 
00045 /*
00046   ===========================================================================
00047 
00048 
00049   S T A R T    O F     T E M P O R A R Y     M I M E     C O D E
00050 
00051 
00052   ===========================================================================
00053   N O T E :   The partNode structure will most likely be replaced by KMime.
00054   It's purpose: Speed optimization for KDE 3.   (khz, 28.11.01)
00055   ===========================================================================
00056 */
00057 
00058 partNode::partNode()
00059   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00060     mWasProcessed( false ),
00061     mDwPart( 0 ),
00062     mType( DwMime::kTypeUnknown ),
00063     mSubType( DwMime::kSubtypeUnknown ),
00064     mEncryptionState( KMMsgNotEncrypted ),
00065     mSignatureState( KMMsgNotSigned ),
00066     mMsgPartOk( false ),
00067     mEncodedOk( false ),
00068     mDeleteDwBodyPart( false ),
00069     mMimePartTreeItem( 0 ),
00070     mBodyPartMementoMap(),
00071     mReader( 0 ),
00072     mDisplayedEmbedded( false )
00073 {
00074   adjustDefaultType( this );
00075 }
00076 
00077 partNode::partNode( KMReaderWin * win, DwBodyPart* dwPart, int explicitType, int explicitSubType,
00078             bool deleteDwBodyPart )
00079   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00080     mWasProcessed( false ),
00081     mDwPart( dwPart ),
00082     mEncryptionState( KMMsgNotEncrypted ),
00083     mSignatureState( KMMsgNotSigned ),
00084     mMsgPartOk( false ),
00085     mEncodedOk( false ),
00086     mDeleteDwBodyPart( deleteDwBodyPart ),
00087     mMimePartTreeItem( 0 ),
00088     mBodyPartMementoMap(),
00089     mReader( win ),
00090     mDisplayedEmbedded( false ),
00091     mDisplayedHidden( false )
00092 {
00093   if ( explicitType != DwMime::kTypeUnknown ) {
00094     mType    = explicitType;     // this happens e.g. for the Root Node
00095     mSubType = explicitSubType;  // representing the _whole_ message
00096   } else {
00097     if(dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType()) {
00098       mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00099       mSubType = dwPart->Headers().ContentType().Subtype();
00100     } else {
00101       mType    = DwMime::kTypeUnknown;
00102       mSubType = DwMime::kSubtypeUnknown;
00103     }
00104   }
00105 }
00106 
00107 partNode * partNode::fromMessage( const KMMessage * msg, KMReaderWin * win ) {
00108   if ( !msg )
00109     return 0;
00110 
00111   int mainType    = msg->type();
00112   int mainSubType = msg->subtype();
00113   if(    (DwMime::kTypeNull    == mainType)
00114       || (DwMime::kTypeUnknown == mainType) ){
00115     mainType    = DwMime::kTypeText;
00116     mainSubType = DwMime::kSubtypePlain;
00117   }
00118 
00119   // we don't want to treat the top-level part special. mimelib does
00120   // (Message vs. BodyPart, with common base class Entity). But we
00121   // used DwBodyPart, not DwEntiy everywhere. *shrug*. DwStrings are
00122   // subscrib-shared, so we just force mimelib to parse the whole mail
00123   // as just another DwBodyPart...
00124   DwBodyPart * mainBody = new DwBodyPart( *msg->getTopLevelPart() );
00125 
00126   partNode * root = new partNode( win, mainBody, mainType, mainSubType, true );
00127   root->buildObjectTree();
00128 
00129   root->setFromAddress( msg->from() );
00130   //root->dump();
00131   return root;
00132 }
00133 
00134 partNode::partNode( bool deleteDwBodyPart, DwBodyPart* dwPart )
00135   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00136     mWasProcessed( false ),
00137     mDwPart( dwPart ),
00138     mEncryptionState( KMMsgNotEncrypted ),
00139     mSignatureState( KMMsgNotSigned ),
00140     mMsgPartOk( false ),
00141     mEncodedOk( false ),
00142     mDeleteDwBodyPart( deleteDwBodyPart ),
00143     mMimePartTreeItem( 0 ),
00144     mBodyPartMementoMap(),
00145     mReader( 0 ),
00146     mDisplayedEmbedded( false )
00147 {
00148   if ( dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType() ) {
00149     mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00150     mSubType = dwPart->Headers().ContentType().Subtype();
00151   } else {
00152     mType    = DwMime::kTypeUnknown;
00153     mSubType = DwMime::kSubtypeUnknown;
00154   }
00155 }
00156 
00157 partNode::~partNode() {
00158   if( mDeleteDwBodyPart )
00159     delete mDwPart;
00160   mDwPart = 0;
00161   delete mChild; mChild = 0;
00162   delete mNext; mNext = 0;
00163   for ( std::map<TQCString,KMail::Interface::BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it )
00164       delete it->second;
00165   mBodyPartMementoMap.clear();
00166 }
00167 
00168 #ifndef NDEBUG
00169 void partNode::dump( int chars ) const {
00170   kdDebug(5006) << nodeId() << " " << TQString(TQString().fill( ' ', chars )) << "+ "
00171                 << typeString() << '/' << subTypeString() << " embedded:" << mDisplayedEmbedded
00172                 << " address:" << this << endl;
00173   if ( mChild )
00174     mChild->dump( chars + 1 );
00175   if ( mNext )
00176     mNext->dump( chars );
00177 }
00178 #else
00179 void partNode::dump( int ) const {}
00180 #endif
00181 
00182 const TQCString & partNode::encodedBody() {
00183   if ( mEncodedOk )
00184     return mEncodedBody;
00185 
00186   if ( mDwPart )
00187     mEncodedBody = KMail::Util::CString( mDwPart->Body().AsString() );
00188   else
00189     mEncodedBody = 0;
00190   mEncodedOk = true;
00191   return mEncodedBody;
00192 }
00193 
00194 
00195 void partNode::buildObjectTree( bool processSiblings )
00196 {
00197     partNode* curNode = this;
00198     while( curNode && curNode->dwPart() ) {
00199         //dive into multipart messages
00200         while( DwMime::kTypeMultipart == curNode->type() ) {
00201             partNode * newNode = new partNode( mReader, curNode->dwPart()->Body().FirstBodyPart() );
00202             curNode->setFirstChild( newNode );
00203             curNode = newNode;
00204         }
00205         // go up in the tree until reaching a node with next
00206         // (or the last top-level node)
00207         while(     curNode
00208                && !(    curNode->dwPart()
00209                      && curNode->dwPart()->Next() ) ) {
00210             curNode = curNode->mRoot;
00211         }
00212         // we might have to leave when all children have been processed
00213         if( this == curNode && !processSiblings )
00214             return;
00215         // store next node
00216         if( curNode && curNode->dwPart() && curNode->dwPart()->Next() ) {
00217             partNode* nextNode = new partNode( mReader, curNode->dwPart()->Next() );
00218             curNode->setNext( nextNode );
00219             curNode = nextNode;
00220         } else
00221             curNode = 0;
00222     }
00223 }
00224 
00225 TQCString partNode::typeString() const {
00226   DwString s;
00227   DwTypeEnumToStr( type(), s );
00228   return s.c_str();
00229 }
00230 
00231 TQCString partNode::subTypeString() const {
00232   DwString s;
00233   DwSubtypeEnumToStr( subType(), s );
00234   return s.c_str();
00235 }
00236 
00237 const partNode* partNode::topLevelParent() const {
00238   const partNode *ret = this;
00239   while ( ret->parentNode() )
00240     ret = ret->parentNode();
00241   return ret;
00242 }
00243 
00244 int partNode::childCount() const {
00245   int count = 0;
00246   for ( partNode * child = firstChild() ; child ; child = child->nextSibling() )
00247     ++ count;
00248   return count;
00249 }
00250 
00251 int partNode::totalChildCount() const {
00252   int count = 0;
00253   for ( partNode * child = firstChild() ; child ; child = child->nextSibling() ) {
00254     ++count;
00255     count += child->totalChildCount();
00256   }
00257   return count;
00258 }
00259 
00260 TQString partNode::contentTypeParameter( const char * name ) const {
00261   if ( !mDwPart || !mDwPart->hasHeaders() )
00262     return TQString();
00263   DwHeaders & headers = mDwPart->Headers();
00264   if ( !headers.HasContentType() )
00265     return TQString();
00266   DwString attr = name;
00267   attr.ConvertToLowerCase();
00268   for ( DwParameter * param = headers.ContentType().FirstParameter() ; param ; param = param->Next() ) {
00269     DwString this_attr = param->Attribute();
00270     this_attr.ConvertToLowerCase(); // what a braindead design!
00271     if ( this_attr == attr )
00272       return TQString::fromLatin1( param->Value().data(), param->Value().size() );
00273     // warning: misses rfc2231 handling!
00274   }
00275   return TQString();
00276 }
00277 
00278 KMMsgEncryptionState partNode::overallEncryptionState() const
00279 {
00280     KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown;
00281     if( mEncryptionState == KMMsgNotEncrypted ) {
00282         // NOTE: children are tested ONLY when parent is not encrypted
00283         if( mChild )
00284             myState = mChild->overallEncryptionState();
00285         else
00286             myState = KMMsgNotEncrypted;
00287     }
00288     else { // part is partially or fully encrypted
00289         myState = mEncryptionState;
00290     }
00291     // siblings are tested always
00292     if( mNext ) {
00293         KMMsgEncryptionState otherState = mNext->overallEncryptionState();
00294         switch( otherState ) {
00295         case KMMsgEncryptionStateUnknown:
00296             break;
00297         case KMMsgNotEncrypted:
00298             if( myState == KMMsgFullyEncrypted )
00299                 myState = KMMsgPartiallyEncrypted;
00300             else if( myState != KMMsgPartiallyEncrypted )
00301                 myState = KMMsgNotEncrypted;
00302             break;
00303         case KMMsgPartiallyEncrypted:
00304             myState = KMMsgPartiallyEncrypted;
00305             break;
00306         case KMMsgFullyEncrypted:
00307             if( myState != KMMsgFullyEncrypted )
00308                 myState = KMMsgPartiallyEncrypted;
00309             break;
00310         case KMMsgEncryptionProblematic:
00311             break;
00312         }
00313     }
00314 
00315     return myState;
00316 }
00317 
00318 
00319 KMMsgSignatureState  partNode::overallSignatureState() const
00320 {
00321     KMMsgSignatureState myState = KMMsgSignatureStateUnknown;
00322     if( mSignatureState == KMMsgNotSigned ) {
00323         // children are tested ONLY when parent is not signed
00324         if( mChild )
00325             myState = mChild->overallSignatureState();
00326         else
00327             myState = KMMsgNotSigned;
00328     }
00329     else { // part is partially or fully signed
00330         myState = mSignatureState;
00331     }
00332     // siblings are tested always
00333     if( mNext ) {
00334         KMMsgSignatureState otherState = mNext->overallSignatureState();
00335         switch( otherState ) {
00336         case KMMsgSignatureStateUnknown:
00337             break;
00338         case KMMsgNotSigned:
00339             if( myState == KMMsgFullySigned )
00340                 myState = KMMsgPartiallySigned;
00341             else if( myState != KMMsgPartiallySigned )
00342                 myState = KMMsgNotSigned;
00343             break;
00344         case KMMsgPartiallySigned:
00345             myState = KMMsgPartiallySigned;
00346             break;
00347         case KMMsgFullySigned:
00348             if( myState != KMMsgFullySigned )
00349                 myState = KMMsgPartiallySigned;
00350             break;
00351         case KMMsgEncryptionProblematic:
00352             break;
00353         }
00354     }
00355 
00356     return myState;
00357 }
00358 
00359 TQCString partNode::path() const
00360 {
00361     if ( !parentNode() )
00362         return ':';
00363     const partNode * p = parentNode();
00364 
00365     // count number of siblings with the same type as us:
00366     int nth = 0;
00367     for ( const partNode * c = p->firstChild() ; c != this ; c = c->nextSibling() )
00368         if ( c->type() == type() && c->subType() == subType() )
00369             ++nth;
00370 
00371     return p->path() + TQCString().sprintf( ":%X/%X[%X]", type(), subType(), nth );
00372 }
00373 
00374 
00375 int partNode::nodeId() const
00376 {
00377     int curId = 0;
00378     partNode* rootNode = const_cast<partNode*>( this );
00379     while( rootNode->mRoot )
00380         rootNode = rootNode->mRoot;
00381     return rootNode->calcNodeIdOrFindNode( curId, this, 0, 0 );
00382 }
00383 
00384 
00385 partNode* partNode::findId( int id )
00386 {
00387     int curId = 0;
00388     partNode* rootNode = this;
00389     while( rootNode->mRoot )
00390         rootNode = rootNode->mRoot;
00391     partNode* foundNode;
00392     rootNode->calcNodeIdOrFindNode( curId, 0, id, &foundNode );
00393     return foundNode;
00394 }
00395 
00396 
00397 int partNode::calcNodeIdOrFindNode( int &curId, const partNode* findNode, int findId, partNode** foundNode )
00398 {
00399     // We use the same algorithm to determine the id of a node and
00400     //                           to find the node when id is known.
00401     curId++;
00402     // check for node ?
00403     if( findNode && this == findNode )
00404         return curId;
00405     // check for id ?
00406     if(  foundNode && curId == findId ) {
00407         *foundNode = this;
00408         return curId;
00409     }
00410     if( mChild )
00411     {
00412         int res = mChild->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00413         if (res != -1) return res;
00414     }
00415     if( mNext )
00416         return mNext->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00417 
00418     if(  foundNode )
00419         *foundNode = 0;
00420     return -1;
00421 }
00422 
00423 
00424 partNode* partNode::findType( int type, int subType, bool deep, bool wide )
00425 {
00426     if(    (mType != DwMime::kTypeUnknown)
00427            && (    (type == DwMime::kTypeUnknown)
00428                    || (type == mType) )
00429            && (    (subType == DwMime::kSubtypeUnknown)
00430                    || (subType == mSubType) ) )
00431         return this;
00432     if ( mChild && deep )
00433         return mChild->findType( type, subType, deep, wide );
00434     if ( mNext && wide )
00435         return mNext->findType(  type, subType, deep, wide );
00436     return 0;
00437 }
00438 
00439 partNode* partNode::findNodeForDwPart( DwBodyPart* part )
00440 {
00441     partNode* found = 0;
00442     if( kasciistricmp( dwPart()->partId(), part->partId() ) == 0 )
00443         return this;
00444     if( mChild )
00445         found = mChild->findNodeForDwPart( part );
00446     if( mNext && !found )
00447         found = mNext->findNodeForDwPart( part );
00448     return found;
00449 }
00450 
00451 partNode* partNode::findTypeNot( int type, int subType, bool deep, bool wide )
00452 {
00453     if(    (mType != DwMime::kTypeUnknown)
00454            && (    (type == DwMime::kTypeUnknown)
00455                    || (type != mType) )
00456            && (    (subType == DwMime::kSubtypeUnknown)
00457                    || (subType != mSubType) ) )
00458         return this;
00459     if ( mChild && deep )
00460         return mChild->findTypeNot( type, subType, deep, wide );
00461     if ( mNext && wide )
00462         return mNext->findTypeNot(  type, subType, deep, wide );
00463     return 0;
00464 }
00465 
00466 void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem,
00467                                  KMMimePartTree*     mimePartTree,
00468                                  TQString labelDescr,
00469                                  TQString labelCntType,
00470                                  TQString labelEncoding,
00471                                  TDEIO::filesize_t size,
00472                                  bool revertOrder )
00473 {
00474   if( parentItem || mimePartTree ) {
00475 
00476     if( mNext )
00477         mNext->fillMimePartTree( parentItem, mimePartTree,
00478                                  TQString(), TQString(), TQString(), 0,
00479                                  revertOrder );
00480 
00481     TQString cntDesc, cntType, cntEnc;
00482     TDEIO::filesize_t cntSize = 0;
00483 
00484     if( labelDescr.isEmpty() ) {
00485         DwHeaders* headers = 0;
00486         if( mDwPart && mDwPart->hasHeaders() )
00487           headers = &mDwPart->Headers();
00488         if( headers && headers->HasSubject() )
00489             cntDesc = KMMsgBase::decodeRFC2047String( headers->Subject().AsString().c_str() );
00490         if( headers && headers->HasContentType()) {
00491             cntType = headers->ContentType().TypeStr().c_str();
00492             cntType += '/';
00493             cntType += headers->ContentType().SubtypeStr().c_str();
00494         }
00495         else
00496             cntType = "text/plain";
00497         if( cntDesc.isEmpty() )
00498             cntDesc = msgPart().name().stripWhiteSpace();
00499         if( cntDesc.isEmpty() )
00500             cntDesc = msgPart().fileName();
00501         if( cntDesc.isEmpty() )
00502             cntDesc = msgPart().contentDescription();
00503         if( cntDesc.isEmpty() ) {
00504             if( mRoot && mRoot->mRoot )
00505                 cntDesc = i18n("internal part");
00506             else
00507                 cntDesc = i18n("body part");
00508         }
00509         cntEnc = msgPart().contentTransferEncodingStr();
00510         if( mDwPart )
00511             cntSize = mDwPart->BodySize();
00512     } else {
00513         cntDesc = labelDescr;
00514         cntType = labelCntType;
00515         cntEnc  = labelEncoding;
00516         cntSize = size;
00517     }
00518     // remove linebreak+whitespace from folded Content-Description
00519     cntDesc.replace( TQRegExp("\\n\\s*"), " " );
00520 
00521     if( parentItem )
00522       mMimePartTreeItem = new KMMimePartTreeItem( parentItem,
00523                                                   this,
00524                                                   cntDesc,
00525                                                   cntType,
00526                                                   cntEnc,
00527                                                   cntSize,
00528                                                   revertOrder );
00529     else if( mimePartTree )
00530       mMimePartTreeItem = new KMMimePartTreeItem( mimePartTree,
00531                                                   this,
00532                                                   cntDesc,
00533                                                   cntType,
00534                                                   cntEnc,
00535                                                   cntSize );
00536     mMimePartTreeItem->setOpen( true );
00537     if( mChild )
00538         mChild->fillMimePartTree( mMimePartTreeItem, 0,
00539                                   TQString(), TQString(), TQString(), 0,
00540                                   revertOrder );
00541 
00542   }
00543 }
00544 
00545 void partNode::adjustDefaultType( partNode* node )
00546 {
00547     // Only bodies of  'Multipart/Digest'  objects have
00548     // default type 'Message/RfC822'.  All other bodies
00549     // have default type 'Text/Plain'  (khz, 5.12.2001)
00550     if( node && DwMime::kTypeUnknown == node->type() ) {
00551         if(    node->mRoot
00552                && DwMime::kTypeMultipart == node->mRoot->type()
00553                && DwMime::kSubtypeDigest == node->mRoot->subType() ) {
00554             node->setType(    DwMime::kTypeMessage   );
00555             node->setSubType( DwMime::kSubtypeRfc822 );
00556         }
00557         else
00558             {
00559                 node->setType(    DwMime::kTypeText     );
00560                 node->setSubType( DwMime::kSubtypePlain );
00561             }
00562     }
00563 }
00564 
00565 bool partNode::isAttachment() const
00566 {
00567   if( !dwPart() )
00568     return false;
00569   if ( !dwPart()->hasHeaders() )
00570     return false;
00571   DwHeaders& headers = dwPart()->Headers();
00572   if ( headers.HasContentType() &&
00573        headers.ContentType().Type() == DwMime::kTypeMessage &&
00574        headers.ContentType().Subtype() == DwMime::kSubtypeRfc822 ) {
00575     // Messages are always attachments. Normally message attachments created from KMail have a content
00576     // disposition, but some mail clients omit that.
00577     return true;
00578   }
00579   if( !headers.HasContentDisposition() )
00580     return false;
00581   return ( headers.ContentDisposition().DispositionType()
00582        == DwMime::kDispTypeAttachment );
00583 }
00584 
00585 bool partNode::isHeuristicalAttachment() const {
00586   if ( isAttachment() )
00587     return true;
00588   const KMMessagePart & p = msgPart();
00589   return !p.fileName().isEmpty() || !p.name().isEmpty() ;
00590 }
00591 
00592 partNode * partNode::next( bool allowChildren ) const {
00593   if ( allowChildren )
00594     if ( partNode * c = firstChild() )
00595       return c;
00596   if ( partNode * s = nextSibling() )
00597     return s;
00598   for ( partNode * p = parentNode() ; p ; p = p->parentNode() )
00599     if ( partNode * s = p->nextSibling() )
00600       return s;
00601   return 0;
00602 }
00603 
00604 bool partNode::isFirstTextPart() const {
00605   if ( type() != DwMime::kTypeText )
00606     return false;
00607   const partNode * root = this;
00608   // go up until we reach the root node of a message (of the actual message or
00609   // of an attached message)
00610   while ( const partNode * p = root->parentNode() ) {
00611     if ( p->type() == DwMime::kTypeMessage )
00612       break;
00613     else
00614       root = p;
00615   }
00616   for ( const partNode * n = root ; n ; n = n->next() )
00617     if ( n->type() == DwMime::kTypeText )
00618       return n == this;
00619   kdFatal() << "partNode::isFirstTextPart(): Didn't expect to end up here..." << endl;
00620   return false; // make comiler happy
00621 }
00622 
00623 bool partNode::isToltecMessage() const
00624 {
00625   if ( type() != DwMime::kTypeMultipart || subType() != DwMime::kSubtypeMixed )
00626     return false;
00627 
00628   if ( childCount() != 3 )
00629     return false;
00630 
00631   const DwField* library = dwPart()->Headers().FindField( "X-Library" );
00632   if ( !library )
00633     return false;
00634 
00635   if ( !library->FieldBody() ||
00636        TQString( library->FieldBody()->AsString().c_str() ) != TQString( "Toltec" ) )
00637     return false;
00638 
00639   const DwField* kolabType = dwPart()->Headers().FindField( "X-Kolab-Type" );
00640   if ( !kolabType )
00641     return false;
00642 
00643   if ( !kolabType->FieldBody() ||
00644        !TQString( kolabType->FieldBody()->AsString().c_str() ).startsWith( "application/x-vnd.kolab" ) )
00645     return false;
00646 
00647   return true;
00648 }
00649 
00650 bool partNode::isInEncapsulatedMessage() const
00651 {
00652   const partNode * const topLevel = topLevelParent();
00653   const partNode *cur = this;
00654   while ( cur && cur != topLevel ) {
00655     const bool parentIsMessage = cur->parentNode() &&
00656                                  cur->parentNode()->msgPart().typeStr().lower() == "message";
00657     if ( parentIsMessage && cur->parentNode() != topLevel )
00658       return true;
00659     cur = cur->parentNode();
00660   }
00661   return false;
00662 }
00663 
00664 bool partNode::hasContentDispositionInline() const
00665 {
00666   if( !dwPart() )
00667     return false;
00668   DwHeaders& headers = dwPart()->Headers();
00669   if( headers.HasContentDisposition() )
00670     return ( headers.ContentDisposition().DispositionType()
00671              == DwMime::kDispTypeInline );
00672   else
00673     return false;
00674 }
00675 
00676 const TQString& partNode::trueFromAddress() const
00677 {
00678   const partNode* node = this;
00679   while( node->mFromAddress.isEmpty() && node->mRoot )
00680     node = node->mRoot;
00681   return node->mFromAddress;
00682 }
00683 
00684 KMail::Interface::BodyPartMemento * partNode::bodyPartMemento( const TQCString & which ) const
00685 {
00686     if ( const KMReaderWin * r = reader() )
00687         return r->bodyPartMemento( this, which );
00688     else
00689         return internalBodyPartMemento( which );
00690 }
00691 
00692 KMail::Interface::BodyPartMemento * partNode::internalBodyPartMemento( const TQCString & which ) const
00693 {
00694     assert( !reader() );
00695 
00696     const std::map<TQCString,KMail::Interface::BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.find( which.lower() );
00697     return it != mBodyPartMementoMap.end() ? it->second : 0 ;
00698 }
00699 
00700 void partNode::setBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento )
00701 {
00702     if ( KMReaderWin * r = reader() )
00703         r->setBodyPartMemento( this, which, memento );
00704     else
00705         internalSetBodyPartMemento( which, memento );
00706 }
00707 
00708 void partNode::internalSetBodyPartMemento( const TQCString & which, KMail::Interface::BodyPartMemento * memento )
00709 {
00710     assert( !reader() );
00711 
00712     const std::map<TQCString,KMail::Interface::BodyPartMemento*>::iterator it = mBodyPartMementoMap.lower_bound( which.lower() );
00713     if ( it != mBodyPartMementoMap.end() && it->first == which.lower() ) {
00714         delete it->second;
00715         if ( memento ) {
00716             it->second = memento;
00717         }
00718         else {
00719             mBodyPartMementoMap.erase( it );
00720         }
00721     } else {
00722         mBodyPartMementoMap.insert( it, std::make_pair( which.lower(), memento ) );
00723     }
00724 }
00725 
00726 bool partNode::isDisplayedEmbedded() const
00727 {
00728   return mDisplayedEmbedded;
00729 }
00730 
00731 void partNode::setDisplayedEmbedded( bool displayedEmbedded )
00732 {
00733   mDisplayedEmbedded = displayedEmbedded;
00734 }
00735 
00736 bool partNode::isDisplayedHidden() const
00737 {
00738   return mDisplayedHidden;
00739 }
00740 
00741 void partNode::setDisplayedHidden( bool displayedHidden )
00742 {
00743   mDisplayedHidden = displayedHidden;
00744 }
00745 
00746 
00747 TQString partNode::asHREF( const TQString &place ) const
00748 {
00749   return TQString( "attachment:%1?place=%2" ).arg( nodeId() ).arg( place );
00750 }
00751 
00752 partNode::AttachmentDisplayInfo partNode::attachmentDisplayInfo() const
00753 {
00754   AttachmentDisplayInfo info;
00755   info.icon = msgPart().iconName( TDEIcon::Small );
00756   info.label = msgPart().name().stripWhiteSpace();
00757   if ( info.label.isEmpty() )
00758     info.label = msgPart().fileName();
00759   if ( info.label.isEmpty() )
00760     info.label = msgPart().contentDescription();
00761   bool typeBlacklisted = msgPart().typeStr().lower() == "multipart";
00762   if ( !typeBlacklisted && msgPart().typeStr().lower() == "application" ) {
00763     typeBlacklisted = msgPart().subtypeStr() == "pgp-encrypted"
00764         || msgPart().subtypeStr().lower() == "pgp-signature"
00765         || msgPart().subtypeStr().lower() == "pkcs7-mime"
00766         || msgPart().subtypeStr().lower() == "pkcs7-signature";
00767   }
00768   typeBlacklisted = typeBlacklisted || this == topLevelParent();
00769   bool firstTextChildOfEncapsulatedMsg = msgPart().typeStr().lower() == "text" &&
00770                                          msgPart().subtypeStr().lower() == "plain" &&
00771                                          parentNode() &&
00772                                          parentNode()->msgPart().typeStr().lower() == "message";
00773   typeBlacklisted = typeBlacklisted || firstTextChildOfEncapsulatedMsg;
00774   info.displayInHeader = !info.label.isEmpty() && !info.icon.isEmpty() && !typeBlacklisted;
00775   return info;
00776 }