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