kmail

partNode.cpp

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