kmail Library API Documentation

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #include "partNode.h"
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036 #include "kmmimeparttree.h"
00037 #include <mimelib/utility.h>
00038 #include <qregexp.h>
00039 
00040 /*
00041   ===========================================================================
00042 
00043 
00044   S T A R T    O F     T E M P O R A R Y     M I M E     C O D E
00045 
00046 
00047   ===========================================================================
00048   N O T E :   The partNode structure will most likely be replaced by KMime.
00049   It's purpose: Speed optimization for KDE 3.   (khz, 28.11.01)
00050   ===========================================================================
00051 */
00052 
00053 partNode::partNode()
00054   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00055     mWasProcessed( false ),
00056     mDwPart( 0 ),
00057     mType( DwMime::kTypeUnknown ),
00058     mSubType( DwMime::kSubtypeUnknown ),
00059     mEncryptionState( KMMsgNotEncrypted ),
00060     mSignatureState( KMMsgNotSigned ),
00061     mMsgPartOk( false ),
00062     mEncodedOk( false ),
00063     mDeleteDwBodyPart( false ),
00064     mMimePartTreeItem( 0 ),
00065     mBodyPartMemento( 0 )
00066 {
00067   adjustDefaultType( this );
00068 }
00069 
00070 partNode::partNode( DwBodyPart* dwPart, int explicitType, int explicitSubType,
00071             bool deleteDwBodyPart )
00072   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00073     mWasProcessed( false ),
00074     mDwPart( dwPart ),
00075     mEncryptionState( KMMsgNotEncrypted ),
00076     mSignatureState( KMMsgNotSigned ),
00077     mMsgPartOk( false ),
00078     mEncodedOk( false ),
00079     mDeleteDwBodyPart( deleteDwBodyPart ),
00080     mMimePartTreeItem( 0 ),
00081     mBodyPartMemento( 0 )
00082 {
00083   if ( explicitType != DwMime::kTypeUnknown ) {
00084     mType    = explicitType;     // this happens e.g. for the Root Node
00085     mSubType = explicitSubType;  // representing the _whole_ message
00086   } else {
00087     kdDebug(5006) << "\n        partNode::partNode()      explicitType == DwMime::kTypeUnknown\n" << endl;
00088     if(dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType()) {
00089       mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00090       mSubType = dwPart->Headers().ContentType().Subtype();
00091     } else {
00092       mType    = DwMime::kTypeUnknown;
00093       mSubType = DwMime::kSubtypeUnknown;
00094     }
00095   }
00096 #ifdef DEBUG
00097   {
00098     DwString type, subType;
00099     DwTypeEnumToStr( mType, type );
00100     DwSubtypeEnumToStr( mSubType, subType );
00101     kdDebug(5006) << "\npartNode::partNode()   " << type.c_str() << "/" << subType.c_str() << "\n" << endl;
00102   }
00103 #endif
00104 }
00105 
00106 partNode * partNode::fromMessage( const KMMessage * msg ) {
00107   if ( !msg )
00108     return 0;
00109 
00110   int mainType    = msg->type();
00111   int mainSubType = msg->subtype();
00112   if(    (DwMime::kTypeNull    == mainType)
00113       || (DwMime::kTypeUnknown == mainType) ){
00114     mainType    = DwMime::kTypeText;
00115     mainSubType = DwMime::kSubtypePlain;
00116   }
00117 
00118   // we don't want to treat the top-level part special. mimelib does
00119   // (Message vs. BodyPart, with common base class Entity). But we
00120   // used DwBodyPart, not DwEntiy everywhere. *shrug*. DwStrings are
00121   // subscrib-shared, so we just force mimelib to parse the whole mail
00122   // as just another DwBodyPart...
00123   DwBodyPart * mainBody = new DwBodyPart( *msg->getTopLevelPart() );
00124 
00125   partNode * root = new partNode( mainBody, mainType, mainSubType, true );
00126   root->buildObjectTree();
00127 
00128   root->setFromAddress( msg->from() );
00129   root->dump();
00130   return root;
00131 }
00132 
00133 partNode::partNode( bool deleteDwBodyPart, DwBodyPart* dwPart )
00134   : mRoot( 0 ), mNext( 0 ), mChild( 0 ),
00135     mWasProcessed( false ),
00136     mDwPart( dwPart ),
00137     mEncryptionState( KMMsgNotEncrypted ),
00138     mSignatureState( KMMsgNotSigned ),
00139     mMsgPartOk( false ),
00140     mEncodedOk( false ),
00141     mDeleteDwBodyPart( deleteDwBodyPart ),
00142     mMimePartTreeItem( 0 ),
00143     mBodyPartMemento( 0 )
00144 {
00145   if ( dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType() ) {
00146     mType    = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type();
00147     mSubType = dwPart->Headers().ContentType().Subtype();
00148   } else {
00149     mType    = DwMime::kTypeUnknown;
00150     mSubType = DwMime::kSubtypeUnknown;
00151   }
00152 }
00153 
00154 partNode::~partNode() {
00155   if( mDeleteDwBodyPart )
00156     delete mDwPart;
00157   mDwPart = 0;
00158   delete mChild; mChild = 0;
00159   delete mNext; mNext = 0;
00160   delete mBodyPartMemento; mBodyPartMemento = 0;
00161 }
00162 
00163 #ifndef NDEBUG
00164 void partNode::dump( int chars ) const {
00165   kdDebug(5006) << QString().fill( ' ', chars ) << "+ "
00166         << typeString() << '/' << subTypeString() << endl;
00167   if ( mChild )
00168     mChild->dump( chars + 1 );
00169   if ( mNext )
00170     mNext->dump( chars );
00171 }
00172 #else
00173 void partNode::dump( int ) const {}
00174 #endif
00175 
00176 const QCString & partNode::encodedBody() {
00177   if ( mEncodedOk )
00178     return mEncodedBody;
00179 
00180   if ( mDwPart )
00181     mEncodedBody = mDwPart->AsString().c_str();
00182   else
00183     mEncodedBody = 0;
00184   mEncodedOk = true;
00185   return mEncodedBody;
00186 }
00187 
00188 
00189 void partNode::buildObjectTree( bool processSiblings )
00190 {
00191     partNode* curNode = this;
00192     while( curNode && curNode->dwPart() ) {
00193         //dive into multipart messages
00194         while( DwMime::kTypeMultipart == curNode->type() ) {
00195             partNode * newNode = new partNode( curNode->dwPart()->Body().FirstBodyPart() );
00196         curNode->setFirstChild( newNode );
00197             curNode = newNode;
00198         }
00199         // go up in the tree until reaching a node with next
00200         // (or the last top-level node)
00201         while(     curNode
00202                && !(    curNode->dwPart()
00203                      && curNode->dwPart()->Next() ) ) {
00204             curNode = curNode->mRoot;
00205         }
00206         // we might have to leave when all children have been processed
00207         if( this == curNode && !processSiblings )
00208             return;
00209         // store next node
00210         if( curNode && curNode->dwPart() && curNode->dwPart()->Next() ) {
00211             partNode* nextNode = new partNode( curNode->dwPart()->Next() );
00212             curNode->setNext( nextNode );
00213         curNode = nextNode;
00214         } else
00215             curNode = 0;
00216     }
00217 }
00218 
00219 QCString partNode::typeString() const {
00220   DwString s;
00221   DwTypeEnumToStr( type(), s );
00222   return s.c_str();
00223 }
00224 
00225 QCString partNode::subTypeString() const {
00226   DwString s;
00227   DwSubtypeEnumToStr( subType(), s );
00228   return s.c_str();
00229 }
00230 
00231 int partNode::childCount() const {
00232   int count = 0;
00233   for ( partNode * child = firstChild() ; child ; child = child->nextSibling() )
00234     ++ count;
00235   return count;
00236 }
00237 
00238 QString partNode::contentTypeParameter( const char * name ) const {
00239   if ( !mDwPart || !mDwPart->hasHeaders() )
00240     return QString::null;
00241   DwHeaders & headers = mDwPart->Headers();
00242   if ( !headers.HasContentType() )
00243     return QString::null;
00244   DwString attr = name;
00245   attr.ConvertToLowerCase();
00246   for ( DwParameter * param = headers.ContentType().FirstParameter() ; param ; param = param->Next() ) {
00247     DwString this_attr = param->Attribute();
00248     this_attr.ConvertToLowerCase(); // what a braindead design!
00249     if ( this_attr == attr )
00250       return QString::fromLatin1( param->Value().data(), param->Value().size() );
00251     // warning: misses rfc2231 handling!
00252   }
00253   return QString::null;
00254 }
00255 
00256 KMMsgEncryptionState partNode::overallEncryptionState() const
00257 {
00258     KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown;
00259     if( mEncryptionState == KMMsgNotEncrypted ) {
00260         // NOTE: children are tested ONLY when parent is not encrypted
00261         if( mChild )
00262             myState = mChild->overallEncryptionState();
00263         else
00264             myState = KMMsgNotEncrypted;
00265     }
00266     else { // part is partially or fully encrypted
00267         myState = mEncryptionState;
00268     }
00269     // siblings are tested always
00270     if( mNext ) {
00271         KMMsgEncryptionState otherState = mNext->overallEncryptionState();
00272         switch( otherState ) {
00273         case KMMsgEncryptionStateUnknown:
00274             break;
00275         case KMMsgNotEncrypted:
00276             if( myState == KMMsgFullyEncrypted )
00277                 myState = KMMsgPartiallyEncrypted;
00278             else if( myState != KMMsgPartiallyEncrypted )
00279                 myState = KMMsgNotEncrypted;
00280             break;
00281         case KMMsgPartiallyEncrypted:
00282             myState = KMMsgPartiallyEncrypted;
00283             break;
00284         case KMMsgFullyEncrypted:
00285             if( myState != KMMsgFullyEncrypted )
00286                 myState = KMMsgPartiallyEncrypted;
00287             break;
00288         case KMMsgEncryptionProblematic:
00289             break;
00290         }
00291     }
00292 
00293 //kdDebug(5006) << "\n\n  KMMsgEncryptionState: " << myState << endl;
00294 
00295     return myState;
00296 }
00297 
00298 
00299 KMMsgSignatureState  partNode::overallSignatureState() const
00300 {
00301     KMMsgSignatureState myState = KMMsgSignatureStateUnknown;
00302     if( mSignatureState == KMMsgNotSigned ) {
00303         // children are tested ONLY when parent is not signed
00304         if( mChild )
00305             myState = mChild->overallSignatureState();
00306         else
00307             myState = KMMsgNotSigned;
00308     }
00309     else { // part is partially or fully signed
00310         myState = mSignatureState;
00311     }
00312     // siblings are tested always
00313     if( mNext ) {
00314         KMMsgSignatureState otherState = mNext->overallSignatureState();
00315         switch( otherState ) {
00316         case KMMsgSignatureStateUnknown:
00317             break;
00318         case KMMsgNotSigned:
00319             if( myState == KMMsgFullySigned )
00320                 myState = KMMsgPartiallySigned;
00321             else if( myState != KMMsgPartiallySigned )
00322                 myState = KMMsgNotSigned;
00323             break;
00324         case KMMsgPartiallySigned:
00325             myState = KMMsgPartiallySigned;
00326             break;
00327         case KMMsgFullySigned:
00328             if( myState != KMMsgFullySigned )
00329                 myState = KMMsgPartiallySigned;
00330             break;
00331         case KMMsgEncryptionProblematic:
00332             break;
00333         }
00334     }
00335 
00336 //kdDebug(5006) << "\n\n  KMMsgSignatureState: " << myState << endl;
00337 
00338     return myState;
00339 }
00340 
00341 
00342 int partNode::nodeId()
00343 {
00344     int curId = 0;
00345     partNode* rootNode = this;
00346     while( rootNode->mRoot )
00347         rootNode = rootNode->mRoot;
00348     return rootNode->calcNodeIdOrFindNode( curId, this, 0, 0 );
00349 }
00350 
00351 
00352 partNode* partNode::findId( int id )
00353 {
00354     int curId = 0;
00355     partNode* rootNode = this;
00356     while( rootNode->mRoot )
00357         rootNode = rootNode->mRoot;
00358     partNode* foundNode;
00359     rootNode->calcNodeIdOrFindNode( curId, 0, id, &foundNode );
00360     return foundNode;
00361 }
00362 
00363 
00364 int partNode::calcNodeIdOrFindNode( int &curId, const partNode* findNode, int findId, partNode** foundNode )
00365 {
00366     // We use the same algorithm to determine the id of a node and
00367     //                           to find the node when id is known.
00368     curId++;
00369     // check for node ?
00370     if( findNode && this == findNode )
00371         return curId;
00372     // check for id ?
00373     if(  foundNode && curId == findId ) {
00374         *foundNode = this;
00375         return curId;
00376     }
00377     if( mChild )
00378     {
00379         int res = mChild->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00380         if (res != -1) return res;
00381     }
00382     if( mNext )
00383         return mNext->calcNodeIdOrFindNode( curId, findNode, findId, foundNode );
00384 
00385     if(  foundNode )
00386         *foundNode = 0;
00387     return -1;
00388 }
00389 
00390 
00391 partNode* partNode::findType( int type, int subType, bool deep, bool wide )
00392 {
00393 #ifndef NDEBUG
00394   DwString typeStr, subTypeStr;
00395   DwTypeEnumToStr( mType, typeStr );
00396   DwSubtypeEnumToStr( mSubType, subTypeStr );
00397   kdDebug(5006) << "partNode::findType() is looking at " << typeStr.c_str()
00398                 << "/" << subTypeStr.c_str() << endl;
00399 #endif
00400     if(    (mType != DwMime::kTypeUnknown)
00401            && (    (type == DwMime::kTypeUnknown)
00402                    || (type == mType) )
00403            && (    (subType == DwMime::kSubtypeUnknown)
00404                    || (subType == mSubType) ) )
00405         return this;
00406     else if( mChild && deep )
00407         return mChild->findType( type, subType, deep, wide );
00408     else if( mNext && wide )
00409         return mNext->findType(  type, subType, deep, wide );
00410     else
00411         return 0;
00412 }
00413 
00414 partNode* partNode::findTypeNot( int type, int subType, bool deep, bool wide )
00415 {
00416     if(    (mType != DwMime::kTypeUnknown)
00417            && (    (type == DwMime::kTypeUnknown)
00418                    || (type != mType) )
00419            && (    (subType == DwMime::kSubtypeUnknown)
00420                    || (subType != mSubType) ) )
00421         return this;
00422     else if( mChild && deep )
00423         return mChild->findTypeNot( type, subType, deep, wide );
00424     else if( mNext && wide )
00425         return mNext->findTypeNot(  type, subType, deep, wide );
00426     else
00427         return 0;
00428 }
00429 
00430 void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem,
00431                                  KMMimePartTree*     mimePartTree,
00432                                  QString labelDescr,
00433                                  QString labelCntType,
00434                                  QString labelEncoding,
00435                                  KIO::filesize_t size,
00436                                  bool revertOrder )
00437 {
00438   if( parentItem || mimePartTree ) {
00439 
00440     if( mNext )
00441         mNext->fillMimePartTree( parentItem, mimePartTree,
00442                                  QString::null, QString::null, QString::null, 0,
00443                                  revertOrder );
00444 
00445     QString cntDesc, cntType, cntEnc;
00446     KIO::filesize_t cntSize = 0;
00447 
00448     if( labelDescr.isEmpty() ) {
00449         DwHeaders* headers = 0;
00450         if( mDwPart && mDwPart->hasHeaders() )
00451           headers = &mDwPart->Headers();
00452         if( headers && headers->HasSubject() )
00453             cntDesc = KMMsgBase::decodeRFC2047String( headers->Subject().AsString().c_str() );
00454         if( headers && headers->HasContentType()) {
00455             cntType = headers->ContentType().TypeStr().c_str();
00456             cntType += '/';
00457             cntType += headers->ContentType().SubtypeStr().c_str();
00458         }
00459         else
00460             cntType = "text/plain";
00461         if( cntDesc.isEmpty() )
00462             cntDesc = msgPart().contentDescription();
00463         if( cntDesc.isEmpty() )
00464             cntDesc = msgPart().name().stripWhiteSpace();
00465         if( cntDesc.isEmpty() )
00466             cntDesc = msgPart().fileName();
00467         if( cntDesc.isEmpty() ) {
00468             if( mRoot && mRoot->mRoot )
00469                 cntDesc = i18n("internal part");
00470             else
00471                 cntDesc = i18n("body part");
00472         }
00473         cntEnc = msgPart().contentTransferEncodingStr();
00474         if( mDwPart )
00475             cntSize = mDwPart->BodySize();
00476     } else {
00477         cntDesc = labelDescr;
00478         cntType = labelCntType;
00479         cntEnc  = labelEncoding;
00480         cntSize = size;
00481     }
00482     // remove linebreak+whitespace from folded Content-Description
00483     cntDesc.replace( QRegExp("\\n\\s*"), " " );
00484 
00485 kdDebug(5006) << "      Inserting one item into MimePartTree" << endl;
00486 kdDebug(5006) << "                Content-Type: " << cntType << endl;
00487     if( parentItem )
00488       mMimePartTreeItem = new KMMimePartTreeItem( parentItem,
00489                                                   this,
00490                                                   cntDesc,
00491                                                   cntType,
00492                                                   cntEnc,
00493                                                   cntSize,
00494                                                   revertOrder );
00495     else if( mimePartTree )
00496       mMimePartTreeItem = new KMMimePartTreeItem( mimePartTree,
00497                                                   this,
00498                                                   cntDesc,
00499                                                   cntType,
00500                                                   cntEnc,
00501                                                   cntSize );
00502     mMimePartTreeItem->setOpen( true );
00503     if( mChild )
00504         mChild->fillMimePartTree( mMimePartTreeItem, 0,
00505                                   QString::null, QString::null, QString::null, 0,
00506                                   revertOrder );
00507 
00508   }
00509 }
00510 
00511 void partNode::adjustDefaultType( partNode* node )
00512 {
00513     // Only bodies of  'Multipart/Digest'  objects have
00514     // default type 'Message/RfC822'.  All other bodies
00515     // have default type 'Text/Plain'  (khz, 5.12.2001)
00516     if( node && DwMime::kTypeUnknown == node->type() ) {
00517         if(    node->mRoot
00518                && DwMime::kTypeMultipart == node->mRoot->type()
00519                && DwMime::kSubtypeDigest == node->mRoot->subType() ) {
00520             node->setType(    DwMime::kTypeMessage   );
00521             node->setSubType( DwMime::kSubtypeRfc822 );
00522         }
00523         else
00524             {
00525                 node->setType(    DwMime::kTypeText     );
00526                 node->setSubType( DwMime::kSubtypePlain );
00527             }
00528     }
00529 }
00530 
00531 bool partNode::isAttachment() const
00532 {
00533   if( !dwPart() )
00534     return false;
00535   if ( !dwPart()->hasHeaders() )
00536     return false;
00537   DwHeaders& headers = dwPart()->Headers();
00538   if( !headers.HasContentDisposition() )
00539     return false;
00540   return ( headers.ContentDisposition().DispositionType()
00541        == DwMime::kDispTypeAttachment );
00542 }
00543 
00544 bool partNode::isHeuristicalAttachment() const {
00545   if ( isAttachment() )
00546     return true;
00547   const KMMessagePart & p = msgPart();
00548   return !p.fileName().isEmpty() || !p.name().isEmpty() ;
00549 }
00550 
00551 partNode * partNode::next( bool allowChildren ) const {
00552   if ( allowChildren )
00553     if ( partNode * c = firstChild() )
00554       return c;
00555   if ( partNode * s = nextSibling() )
00556     return s;
00557   for ( partNode * p = parentNode() ; p ; p = p->parentNode() )
00558     if ( partNode * s = p->nextSibling() )
00559       return s;
00560   return 0;
00561 }
00562 
00563 bool partNode::isFirstTextPart() const {
00564   if ( type() != DwMime::kTypeText )
00565     return false;
00566   const partNode * root = this;
00567   // go up until we reach the root node of a message (of the actual message or
00568   // of an attached message)
00569   while ( const partNode * p = root->parentNode() ) {
00570     if ( p->type() == DwMime::kTypeMessage )
00571       break;
00572     else
00573     root = p;
00574   }
00575   for ( const partNode * n = root ; n ; n = n->next() )
00576     if ( n->type() == DwMime::kTypeText )
00577       return n == this;
00578   kdFatal() << "partNode::isFirstTextPart(): Didn't expect to end up here..." << endl;
00579   return false; // make comiler happy
00580 }
00581 
00582 bool partNode::hasContentDispositionInline() const
00583 {
00584   if( !dwPart() )
00585     return false;
00586   DwHeaders& headers = dwPart()->Headers();
00587   if( headers.HasContentDisposition() )
00588     return ( headers.ContentDisposition().DispositionType()
00589              == DwMime::kDispTypeInline );
00590   else
00591     return false;
00592 }
00593 
00594 const QString& partNode::trueFromAddress() const
00595 {
00596   const partNode* node = this;
00597   while( node->mFromAddress.isEmpty() && node->mRoot )
00598     node = node->mRoot;
00599   return node->mFromAddress;
00600 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 2 09:55:24 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003