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 Qt library by Trolltech AS, Norway (or with modified versions
00023     of Qt that use the same license as Qt), 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     Qt.  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 <klocale.h>
00038 #include <kdebug.h>
00039 #include "kmmimeparttree.h"
00040 #include <mimelib/utility.h>
00041 #include <qregexp.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 {
00073   adjustDefaultType( this );
00074 }
00075 
00076 partNode::partNode( KMReaderWin * win, DwBodyPart* dwPart, int explicitType, int explicitSubType,
00077             bool deleteDwBodyPart )
00078   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00079     mWasProcessed( false ),
00080     mDwPart( dwPart ),
00081     mEncryptionState( KMMsgNotEncrypted ),
00082     mSignatureState( KMMsgNotSigned ),
00083     mMsgPartOk( false ),
00084     mEncodedOk( false ),
00085     mDeleteDwBodyPart( deleteDwBodyPart ),
00086     mMimePartTreeItem( 0 ),
00087     mBodyPartMementoMap(),
00088     mReader( win )
00089 {
00090   if ( explicitType != DwMime::kTypeUnknown ) {
00091     mType    = explicitType;     // this happens e.g. for the Root Node
00092     mSubType = explicitSubType;  // representing the _whole_ message
00093   } else {
00094 //    kdDebug(5006) << "\n        partNode::partNode()      explicitType == DwMime::kTypeUnknown\n" << endl;
00095     if(dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType()) {
00096       mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00097       mSubType = dwPart->Headers().ContentType().Subtype();
00098     } else {
00099       mType    = DwMime::kTypeUnknown;
00100       mSubType = DwMime::kSubtypeUnknown;
00101     }
00102   }
00103 #ifdef DEBUG
00104   {
00105     DwString type, subType;
00106     DwTypeEnumToStr( mType, type );
00107     DwSubtypeEnumToStr( mSubType, subType );
00108     kdDebug(5006) << "\npartNode::partNode()   " << type.c_str() << "/" << subType.c_str() << "\n" << endl;
00109   }
00110 #endif
00111 }
00112 
00113 partNode * partNode::fromMessage( const KMMessage * msg, KMReaderWin * win ) {
00114   if ( !msg )
00115     return 0;
00116 
00117   int mainType    = msg->type();
00118   int mainSubType = msg->subtype();
00119   if(    (DwMime::kTypeNull    == mainType)
00120       || (DwMime::kTypeUnknown == mainType) ){
00121     mainType    = DwMime::kTypeText;
00122     mainSubType = DwMime::kSubtypePlain;
00123   }
00124 
00125   // we don't want to treat the top-level part special. mimelib does
00126   // (Message vs. BodyPart, with common base class Entity). But we
00127   // used DwBodyPart, not DwEntiy everywhere. *shrug*. DwStrings are
00128   // subscrib-shared, so we just force mimelib to parse the whole mail
00129   // as just another DwBodyPart...
00130   DwBodyPart * mainBody = new DwBodyPart( *msg->getTopLevelPart() );
00131 
00132   partNode * root = new partNode( win, mainBody, mainType, mainSubType, true );
00133   root->buildObjectTree();
00134 
00135   root->setFromAddress( msg->from() );
00136   root->dump();
00137   return root;
00138 }
00139 
00140 partNode::partNode( bool deleteDwBodyPart, DwBodyPart* dwPart )
00141   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00142     mWasProcessed( false ),
00143     mDwPart( dwPart ),
00144     mEncryptionState( KMMsgNotEncrypted ),
00145     mSignatureState( KMMsgNotSigned ),
00146     mMsgPartOk( false ),
00147     mEncodedOk( false ),
00148     mDeleteDwBodyPart( deleteDwBodyPart ),
00149     mMimePartTreeItem( 0 ),
00150     mBodyPartMementoMap(),
00151     mReader( 0 )
00152 {
00153   if ( dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType() ) {
00154     mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00155     mSubType = dwPart->Headers().ContentType().Subtype();
00156   } else {
00157     mType    = DwMime::kTypeUnknown;
00158     mSubType = DwMime::kSubtypeUnknown;
00159   }
00160 }
00161 
00162 partNode::~partNode() {
00163   if( mDeleteDwBodyPart )
00164     delete mDwPart;
00165   mDwPart = 0;
00166   delete mChild; mChild = 0;
00167   delete mNext; mNext = 0;
00168   for ( std::map<QCString,KMail::Interface::BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it )
00169       delete it->second;
00170   mBodyPartMementoMap.clear();
00171 }
00172 
00173 #ifndef NDEBUG
00174 void partNode::dump( int chars ) const {
00175   kdDebug(5006) << QString().fill( ' ', chars ) << "+ "
00176         << typeString() << '/' << subTypeString() << endl;
00177   if ( mChild )
00178     mChild->dump( chars + 1 );
00179   if ( mNext )
00180     mNext->dump( chars );
00181 }
00182 #else
00183 void partNode::dump( int ) const {}
00184 #endif
00185 
00186 const QCString & partNode::encodedBody() {
00187   if ( mEncodedOk )
00188     return mEncodedBody;
00189 
00190   if ( mDwPart )
00191     mEncodedBody = KMail::Util::CString( mDwPart->Body().AsString() );
00192   else
00193     mEncodedBody = 0;
00194   mEncodedOk = true;
00195   return mEncodedBody;
00196 }
00197 
00198 
00199 void partNode::buildObjectTree( bool processSiblings )
00200 {
00201     partNode* curNode = this;
00202     while( curNode && curNode->dwPart() ) {
00203         //dive into multipart messages
00204         while( DwMime::kTypeMultipart == curNode->type() ) {
00205             partNode * newNode = new partNode( mReader, curNode->dwPart()->Body().FirstBodyPart() );
00206             curNode->setFirstChild( newNode );
00207             curNode = newNode;
00208         }
00209         // go up in the tree until reaching a node with next
00210         // (or the last top-level node)
00211         while(     curNode
00212                && !(    curNode->dwPart()
00213                      && curNode->dwPart()->Next() ) ) {
00214             curNode = curNode->mRoot;
00215         }
00216         // we might have to leave when all children have been processed
00217         if( this == curNode && !processSiblings )
00218             return;
00219         // store next node
00220         if( curNode && curNode->dwPart() && curNode->dwPart()->Next() ) {
00221             partNode* nextNode = new partNode( mReader, curNode->dwPart()->Next() );
00222             curNode->setNext( nextNode );
00223             curNode = nextNode;
00224         } else
00225             curNode = 0;
00226     }
00227 }
00228 
00229 QCString partNode::typeString() const {
00230   DwString s;
00231   DwTypeEnumToStr( type(), s );
00232   return s.c_str();
00233 }
00234 
00235 QCString partNode::subTypeString() const {
00236   DwString s;
00237   DwSubtypeEnumToStr( subType(), s );
00238   return s.c_str();
00239 }
00240 
00241 int partNode::childCount() const {
00242   int count = 0;
00243   for ( partNode * child = firstChild() ; child ; child = child->nextSibling() )
00244     ++ count;
00245   return count;
00246 }
00247 
00248 QString partNode::contentTypeParameter( const char * name ) const {
00249   if ( !mDwPart || !mDwPart->hasHeaders() )
00250     return QString::null;
00251   DwHeaders & headers = mDwPart->Headers();
00252   if ( !headers.HasContentType() )
00253     return QString::null;
00254   DwString attr = name;
00255   attr.ConvertToLowerCase();
00256   for ( DwParameter * param = headers.ContentType().FirstParameter() ; param ; param = param->Next() ) {
00257     DwString this_attr = param->Attribute();
00258     this_attr.ConvertToLowerCase(); // what a braindead design!
00259     if ( this_attr == attr )
00260       return QString::fromLatin1( param->Value().data(), param->Value().size() );
00261     // warning: misses rfc2231 handling!
00262   }
00263   return QString::null;
00264 }
00265 
00266 KMMsgEncryptionState partNode::overallEncryptionState() const
00267 {
00268     KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown;
00269     if( mEncryptionState == KMMsgNotEncrypted ) {
00270         // NOTE: children are tested ONLY when parent is not encrypted
00271         if( mChild )
00272             myState = mChild->overallEncryptionState();
00273         else
00274             myState = KMMsgNotEncrypted;
00275     }
00276     else { // part is partially or fully encrypted
00277         myState = mEncryptionState;
00278     }
00279     // siblings are tested always
00280     if( mNext ) {
00281         KMMsgEncryptionState otherState = mNext->overallEncryptionState();
00282         switch( otherState ) {
00283         case KMMsgEncryptionStateUnknown:
00284             break;
00285         case KMMsgNotEncrypted:
00286             if( myState == KMMsgFullyEncrypted )
00287                 myState = KMMsgPartiallyEncrypted;
00288             else if( myState != KMMsgPartiallyEncrypted )
00289                 myState = KMMsgNotEncrypted;
00290             break;
00291         case KMMsgPartiallyEncrypted:
00292             myState = KMMsgPartiallyEncrypted;
00293             break;
00294         case KMMsgFullyEncrypted:
00295             if( myState != KMMsgFullyEncrypted )
00296                 myState = KMMsgPartiallyEncrypted;
00297             break;
00298         case KMMsgEncryptionProblematic:
00299             break;
00300         }
00301     }
00302 
00303 //kdDebug(5006) << "\n\n  KMMsgEncryptionState: " << myState << endl;
00304 
00305     return myState;
00306 }
00307 
00308 
00309 KMMsgSignatureState  partNode::overallSignatureState() const
00310 {
00311     KMMsgSignatureState myState = KMMsgSignatureStateUnknown;
00312     if( mSignatureState == KMMsgNotSigned ) {
00313         // children are tested ONLY when parent is not signed
00314         if( mChild )
00315             myState = mChild->overallSignatureState();
00316         else
00317             myState = KMMsgNotSigned;
00318     }
00319     else { // part is partially or fully signed
00320         myState = mSignatureState;
00321     }
00322     // siblings are tested always
00323     if( mNext ) {
00324         KMMsgSignatureState otherState = mNext->overallSignatureState();
00325         switch( otherState ) {
00326         case KMMsgSignatureStateUnknown:
00327             break;
00328         case KMMsgNotSigned:
00329             if( myState == KMMsgFullySigned )
00330                 myState = KMMsgPartiallySigned;
00331             else if( myState != KMMsgPartiallySigned )
00332                 myState = KMMsgNotSigned;
00333             break;
00334         case KMMsgPartiallySigned:
00335             myState = KMMsgPartiallySigned;
00336             break;
00337         case KMMsgFullySigned:
00338             if( myState != KMMsgFullySigned )
00339                 myState = KMMsgPartiallySigned;
00340             break;
00341         case KMMsgEncryptionProblematic:
00342             break;
00343         }
00344     }
00345 
00346 //kdDebug(5006) << "\n\n  KMMsgSignatureState: " << myState << endl;
00347 
00348     return myState;
00349 }
00350 
00351 QCString partNode::path() const
00352 {
00353     if ( !parentNode() )
00354         return ':';
00355     const partNode * p = parentNode();
00356 
00357     // count number of siblings with the same type as us:
00358     int nth = 0;
00359     for ( const partNode * c = p->firstChild() ; c != this ; c = c->nextSibling() )
00360         if ( c->type() == type() && c->subType() == subType() )
00361             ++nth;
00362 
00363     return p->path() + QCString().sprintf( ":%X/%X[%X]", type(), subType(), nth );
00364 }
00365 
00366 
00367 int partNode::nodeId() const
00368 {
00369     int curId = 0;
00370     partNode* rootNode = const_cast<partNode*>( this );
00371     while( rootNode->mRoot )
00372         rootNode = rootNode->mRoot;
00373     return rootNode->calcNodeIdOrFindNode( curId, this, 0, 0 );
00374 }
00375 
00376 
00377 partNode* partNode::findId( int id )
00378 {
00379     int curId = 0;
00380     partNode* rootNode = this;
00381     while( rootNode->mRoot )
00382         rootNode = rootNode->mRoot;
00383     partNode* foundNode;
00384     rootNode->calcNodeIdOrFindNode( curId, 0, id, &foundNode );
00385     return foundNode;
00386 }
00387 
00388 
00389 int partNode::calcNodeIdOrFindNode( int &curId, const partNode* findNode, int findId, partNode** foundNode )
00390 {
00391     // We use the same algorithm to determine the id of a node and
00392     //                           to find the node when id is known.
00393     curId++;
00394     // check for node ?
00395     if( findNode && this == findNode )
00396         return curId;
00397     // check for id ?
00398     if(  foundNode && curId == findId ) {
00399         *foundNode = this;
00400         return curId;
00401     }
00402     if( mChild )
00403     {
00404         int res = mChild->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00405         if (res != -1) return res;
00406     }
00407     if( mNext )
00408         return mNext->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00409 
00410     if(  foundNode )
00411         *foundNode = 0;
00412     return -1;
00413 }
00414 
00415 
00416 partNode* partNode::findType( int type, int subType, bool deep, bool wide )
00417 {
00418 #ifndef NDEBUG
00419   DwString typeStr, subTypeStr;
00420   DwTypeEnumToStr( mType, typeStr );
00421   DwSubtypeEnumToStr( mSubType, subTypeStr );
00422   kdDebug(5006) << "partNode::findType() is looking at " << typeStr.c_str()
00423                 << "/" << subTypeStr.c_str() << endl;
00424 #endif
00425     if(    (mType != DwMime::kTypeUnknown)
00426            && (    (type == DwMime::kTypeUnknown)
00427                    || (type == mType) )
00428            && (    (subType == DwMime::kSubtypeUnknown)
00429                    || (subType == mSubType) ) )
00430         return this;
00431     if ( mChild && deep )
00432         return mChild->findType( type, subType, deep, wide );
00433     if ( mNext && wide )
00434         return mNext->findType(  type, subType, deep, wide );
00435     return 0;
00436 }
00437 
00438 partNode* partNode::findNodeForDwPart( DwBodyPart* part )
00439 {
00440     partNode* found = 0;
00441     if( kasciistricmp( dwPart()->partId(), part->partId() ) == 0 )
00442         return this;
00443     if( mChild )
00444         found = mChild->findNodeForDwPart( part );
00445     if( mNext && !found )
00446         found = mNext->findNodeForDwPart( part );
00447     return found;
00448 }
00449 
00450 partNode* partNode::findTypeNot( int type, int subType, bool deep, bool wide )
00451 {
00452     if(    (mType != DwMime::kTypeUnknown)
00453            && (    (type == DwMime::kTypeUnknown)
00454                    || (type != mType) )
00455            && (    (subType == DwMime::kSubtypeUnknown)
00456                    || (subType != mSubType) ) )
00457         return this;
00458     if ( mChild && deep )
00459         return mChild->findTypeNot( type, subType, deep, wide );
00460     if ( mNext && wide )
00461         return mNext->findTypeNot(  type, subType, deep, wide );
00462     return 0;
00463 }
00464 
00465 void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem,
00466                                  KMMimePartTree*     mimePartTree,
00467                                  QString labelDescr,
00468                                  QString labelCntType,
00469                                  QString labelEncoding,
00470                                  KIO::filesize_t size,
00471                                  bool revertOrder )
00472 {
00473   if( parentItem || mimePartTree ) {
00474 
00475     if( mNext )
00476         mNext->fillMimePartTree( parentItem, mimePartTree,
00477                                  QString::null, QString::null, QString::null, 0,
00478                                  revertOrder );
00479 
00480     QString cntDesc, cntType, cntEnc;
00481     KIO::filesize_t cntSize = 0;
00482 
00483     if( labelDescr.isEmpty() ) {
00484         DwHeaders* headers = 0;
00485         if( mDwPart && mDwPart->hasHeaders() )
00486           headers = &mDwPart->Headers();
00487         if( headers && headers->HasSubject() )
00488             cntDesc = KMMsgBase::decodeRFC2047String( headers->Subject().AsString().c_str() );
00489         if( headers && headers->HasContentType()) {
00490             cntType = headers->ContentType().TypeStr().c_str();
00491             cntType += '/';
00492             cntType += headers->ContentType().SubtypeStr().c_str();
00493         }
00494         else
00495             cntType = "text/plain";
00496         if( cntDesc.isEmpty() )
00497             cntDesc = msgPart().contentDescription();
00498         if( cntDesc.isEmpty() )
00499             cntDesc = msgPart().name().stripWhiteSpace();
00500         if( cntDesc.isEmpty() )
00501             cntDesc = msgPart().fileName();
00502         if( cntDesc.isEmpty() ) {
00503             if( mRoot && mRoot->mRoot )
00504                 cntDesc = i18n("internal part");
00505             else
00506                 cntDesc = i18n("body part");
00507         }
00508         cntEnc = msgPart().contentTransferEncodingStr();
00509         if( mDwPart )
00510             cntSize = mDwPart->BodySize();
00511     } else {
00512         cntDesc = labelDescr;
00513         cntType = labelCntType;
00514         cntEnc  = labelEncoding;
00515         cntSize = size;
00516     }
00517     // remove linebreak+whitespace from folded Content-Description
00518     cntDesc.replace( QRegExp("\\n\\s*"), " " );
00519 
00520 kdDebug(5006) << "      Inserting one item into MimePartTree" << endl;
00521 kdDebug(5006) << "                Content-Type: " << cntType << endl;
00522     if( parentItem )
00523       mMimePartTreeItem = new KMMimePartTreeItem( parentItem,
00524                                                   this,
00525                                                   cntDesc,
00526                                                   cntType,
00527                                                   cntEnc,
00528                                                   cntSize,
00529                                                   revertOrder );
00530     else if( mimePartTree )
00531       mMimePartTreeItem = new KMMimePartTreeItem( mimePartTree,
00532                                                   this,
00533                                                   cntDesc,
00534                                                   cntType,
00535                                                   cntEnc,
00536                                                   cntSize );
00537     mMimePartTreeItem->setOpen( true );
00538     if( mChild )
00539         mChild->fillMimePartTree( mMimePartTreeItem, 0,
00540                                   QString::null, QString::null, QString::null, 0,
00541                                   revertOrder );
00542 
00543   }
00544 }
00545 
00546 void partNode::adjustDefaultType( partNode* node )
00547 {
00548     // Only bodies of  'Multipart/Digest'  objects have
00549     // default type 'Message/RfC822'.  All other bodies
00550     // have default type 'Text/Plain'  (khz, 5.12.2001)
00551     if( node && DwMime::kTypeUnknown == node->type() ) {
00552         if(    node->mRoot
00553                && DwMime::kTypeMultipart == node->mRoot->type()
00554                && DwMime::kSubtypeDigest == node->mRoot->subType() ) {
00555             node->setType(    DwMime::kTypeMessage   );
00556             node->setSubType( DwMime::kSubtypeRfc822 );
00557         }
00558         else
00559             {
00560                 node->setType(    DwMime::kTypeText     );
00561                 node->setSubType( DwMime::kSubtypePlain );
00562             }
00563     }
00564 }
00565 
00566 bool partNode::isAttachment() const
00567 {
00568   if( !dwPart() )
00569     return false;
00570   if ( !dwPart()->hasHeaders() )
00571     return false;
00572   DwHeaders& headers = dwPart()->Headers();
00573   if( !headers.HasContentDisposition() )
00574     return false;
00575   return ( headers.ContentDisposition().DispositionType()
00576        == DwMime::kDispTypeAttachment );
00577 }
00578 
00579 bool partNode::isHeuristicalAttachment() const {
00580   if ( isAttachment() )
00581     return true;
00582   const KMMessagePart & p = msgPart();
00583   return !p.fileName().isEmpty() || !p.name().isEmpty() ;
00584 }
00585 
00586 partNode * partNode::next( bool allowChildren ) const {
00587   if ( allowChildren )
00588     if ( partNode * c = firstChild() )
00589       return c;
00590   if ( partNode * s = nextSibling() )
00591     return s;
00592   for ( partNode * p = parentNode() ; p ; p = p->parentNode() )
00593     if ( partNode * s = p->nextSibling() )
00594       return s;
00595   return 0;
00596 }
00597 
00598 bool partNode::isFirstTextPart() const {
00599   if ( type() != DwMime::kTypeText )
00600     return false;
00601   const partNode * root = this;
00602   // go up until we reach the root node of a message (of the actual message or
00603   // of an attached message)
00604   while ( const partNode * p = root->parentNode() ) {
00605     if ( p->type() == DwMime::kTypeMessage )
00606       break;
00607     else
00608       root = p;
00609   }
00610   for ( const partNode * n = root ; n ; n = n->next() )
00611     if ( n->type() == DwMime::kTypeText )
00612       return n == this;
00613   kdFatal() << "partNode::isFirstTextPart(): Didn't expect to end up here..." << endl;
00614   return false; // make comiler happy
00615 }
00616 
00617 bool partNode::hasContentDispositionInline() const
00618 {
00619   if( !dwPart() )
00620     return false;
00621   DwHeaders& headers = dwPart()->Headers();
00622   if( headers.HasContentDisposition() )
00623     return ( headers.ContentDisposition().DispositionType()
00624              == DwMime::kDispTypeInline );
00625   else
00626     return false;
00627 }
00628 
00629 const QString& partNode::trueFromAddress() const
00630 {
00631   const partNode* node = this;
00632   while( node->mFromAddress.isEmpty() && node->mRoot )
00633     node = node->mRoot;
00634   return node->mFromAddress;
00635 }
00636 
00637 KMail::Interface::BodyPartMemento * partNode::bodyPartMemento( const QCString & which ) const
00638 {
00639     if ( const KMReaderWin * r = reader() )
00640         return r->bodyPartMemento( this, which );
00641     else
00642         return internalBodyPartMemento( which );
00643 }
00644 
00645 KMail::Interface::BodyPartMemento * partNode::internalBodyPartMemento( const QCString & which ) const
00646 {
00647     assert( !reader() );
00648 
00649     const std::map<QCString,KMail::Interface::BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.find( which.lower() );
00650     return it != mBodyPartMementoMap.end() ? it->second : 0 ;
00651 }
00652 
00653 void partNode::setBodyPartMemento( const QCString & which, KMail::Interface::BodyPartMemento * memento )
00654 {
00655     if ( KMReaderWin * r = reader() )
00656         r->setBodyPartMemento( this, which, memento );
00657     else
00658         internalSetBodyPartMemento( which, memento );
00659 }
00660 
00661 void partNode::internalSetBodyPartMemento( const QCString & which, KMail::Interface::BodyPartMemento * memento )
00662 {
00663     assert( !reader() );
00664 
00665     const std::map<QCString,KMail::Interface::BodyPartMemento*>::iterator it = mBodyPartMementoMap.lower_bound( which.lower() );
00666     if ( it != mBodyPartMementoMap.end() && it->first == which.lower() ) {
00667         delete it->second;
00668         if ( memento )
00669             it->second = memento;
00670         else
00671             mBodyPartMementoMap.erase( it );
00672     } else {
00673         mBodyPartMementoMap.insert( it, std::make_pair( which.lower(), memento ) );
00674     }
00675 }
KDE Home | KDE Accessibility Home | Description of Access Keys