diff options
Diffstat (limited to 'kmail/partNode.cpp')
-rw-r--r-- | kmail/partNode.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/kmail/partNode.cpp b/kmail/partNode.cpp new file mode 100644 index 000000000..4fd51c857 --- /dev/null +++ b/kmail/partNode.cpp @@ -0,0 +1,612 @@ +/* -*- c++ -*- + partNode.cpp A node in a MIME tree. + + This file is part of KMail, the KDE mail client. + Copyright (c) 2002 Klar�lvdalens Datakonsult AB + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include <config.h> +#include "partNode.h" +#include <klocale.h> +#include <kdebug.h> +#include "kmmimeparttree.h" +#include <mimelib/utility.h> +#include <qregexp.h> +#include <kasciistricmp.h> +#include "util.h" + +/* + =========================================================================== + + + S T A R T O F T E M P O R A R Y M I M E C O D E + + + =========================================================================== + N O T E : The partNode structure will most likely be replaced by KMime. + It's purpose: Speed optimization for KDE 3. (khz, 28.11.01) + =========================================================================== +*/ + +partNode::partNode() + : mRoot( 0 ), mNext( 0 ), mChild( 0 ), + mWasProcessed( false ), + mDwPart( 0 ), + mType( DwMime::kTypeUnknown ), + mSubType( DwMime::kSubtypeUnknown ), + mEncryptionState( KMMsgNotEncrypted ), + mSignatureState( KMMsgNotSigned ), + mMsgPartOk( false ), + mEncodedOk( false ), + mDeleteDwBodyPart( false ), + mMimePartTreeItem( 0 ), + mBodyPartMemento( 0 ) +{ + adjustDefaultType( this ); +} + +partNode::partNode( DwBodyPart* dwPart, int explicitType, int explicitSubType, + bool deleteDwBodyPart ) + : mRoot( 0 ), mNext( 0 ), mChild( 0 ), + mWasProcessed( false ), + mDwPart( dwPart ), + mEncryptionState( KMMsgNotEncrypted ), + mSignatureState( KMMsgNotSigned ), + mMsgPartOk( false ), + mEncodedOk( false ), + mDeleteDwBodyPart( deleteDwBodyPart ), + mMimePartTreeItem( 0 ), + mBodyPartMemento( 0 ) +{ + if ( explicitType != DwMime::kTypeUnknown ) { + mType = explicitType; // this happens e.g. for the Root Node + mSubType = explicitSubType; // representing the _whole_ message + } else { +// kdDebug(5006) << "\n partNode::partNode() explicitType == DwMime::kTypeUnknown\n" << endl; + if(dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType()) { + mType = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type(); + mSubType = dwPart->Headers().ContentType().Subtype(); + } else { + mType = DwMime::kTypeUnknown; + mSubType = DwMime::kSubtypeUnknown; + } + } +#ifdef DEBUG + { + DwString type, subType; + DwTypeEnumToStr( mType, type ); + DwSubtypeEnumToStr( mSubType, subType ); + kdDebug(5006) << "\npartNode::partNode() " << type.c_str() << "/" << subType.c_str() << "\n" << endl; + } +#endif +} + +partNode * partNode::fromMessage( const KMMessage * msg ) { + if ( !msg ) + return 0; + + int mainType = msg->type(); + int mainSubType = msg->subtype(); + if( (DwMime::kTypeNull == mainType) + || (DwMime::kTypeUnknown == mainType) ){ + mainType = DwMime::kTypeText; + mainSubType = DwMime::kSubtypePlain; + } + + // we don't want to treat the top-level part special. mimelib does + // (Message vs. BodyPart, with common base class Entity). But we + // used DwBodyPart, not DwEntiy everywhere. *shrug*. DwStrings are + // subscrib-shared, so we just force mimelib to parse the whole mail + // as just another DwBodyPart... + DwBodyPart * mainBody = new DwBodyPart( *msg->getTopLevelPart() ); + + partNode * root = new partNode( mainBody, mainType, mainSubType, true ); + root->buildObjectTree(); + + root->setFromAddress( msg->from() ); + root->dump(); + return root; +} + +partNode::partNode( bool deleteDwBodyPart, DwBodyPart* dwPart ) + : mRoot( 0 ), mNext( 0 ), mChild( 0 ), + mWasProcessed( false ), + mDwPart( dwPart ), + mEncryptionState( KMMsgNotEncrypted ), + mSignatureState( KMMsgNotSigned ), + mMsgPartOk( false ), + mEncodedOk( false ), + mDeleteDwBodyPart( deleteDwBodyPart ), + mMimePartTreeItem( 0 ), + mBodyPartMemento( 0 ) +{ + if ( dwPart && dwPart->hasHeaders() && dwPart->Headers().HasContentType() ) { + mType = (!dwPart->Headers().ContentType().Type())?DwMime::kTypeUnknown:dwPart->Headers().ContentType().Type(); + mSubType = dwPart->Headers().ContentType().Subtype(); + } else { + mType = DwMime::kTypeUnknown; + mSubType = DwMime::kSubtypeUnknown; + } +} + +partNode::~partNode() { + if( mDeleteDwBodyPart ) + delete mDwPart; + mDwPart = 0; + delete mChild; mChild = 0; + delete mNext; mNext = 0; + delete mBodyPartMemento; mBodyPartMemento = 0; +} + +#ifndef NDEBUG +void partNode::dump( int chars ) const { + kdDebug(5006) << QString().fill( ' ', chars ) << "+ " + << typeString() << '/' << subTypeString() << endl; + if ( mChild ) + mChild->dump( chars + 1 ); + if ( mNext ) + mNext->dump( chars ); +} +#else +void partNode::dump( int ) const {} +#endif + +const QCString & partNode::encodedBody() { + if ( mEncodedOk ) + return mEncodedBody; + + if ( mDwPart ) + mEncodedBody = KMail::Util::CString( mDwPart->Body().AsString() ); + else + mEncodedBody = 0; + mEncodedOk = true; + return mEncodedBody; +} + + +void partNode::buildObjectTree( bool processSiblings ) +{ + partNode* curNode = this; + while( curNode && curNode->dwPart() ) { + //dive into multipart messages + while( DwMime::kTypeMultipart == curNode->type() ) { + partNode * newNode = new partNode( curNode->dwPart()->Body().FirstBodyPart() ); + curNode->setFirstChild( newNode ); + curNode = newNode; + } + // go up in the tree until reaching a node with next + // (or the last top-level node) + while( curNode + && !( curNode->dwPart() + && curNode->dwPart()->Next() ) ) { + curNode = curNode->mRoot; + } + // we might have to leave when all children have been processed + if( this == curNode && !processSiblings ) + return; + // store next node + if( curNode && curNode->dwPart() && curNode->dwPart()->Next() ) { + partNode* nextNode = new partNode( curNode->dwPart()->Next() ); + curNode->setNext( nextNode ); + curNode = nextNode; + } else + curNode = 0; + } +} + +QCString partNode::typeString() const { + DwString s; + DwTypeEnumToStr( type(), s ); + return s.c_str(); +} + +QCString partNode::subTypeString() const { + DwString s; + DwSubtypeEnumToStr( subType(), s ); + return s.c_str(); +} + +int partNode::childCount() const { + int count = 0; + for ( partNode * child = firstChild() ; child ; child = child->nextSibling() ) + ++ count; + return count; +} + +QString partNode::contentTypeParameter( const char * name ) const { + if ( !mDwPart || !mDwPart->hasHeaders() ) + return QString::null; + DwHeaders & headers = mDwPart->Headers(); + if ( !headers.HasContentType() ) + return QString::null; + DwString attr = name; + attr.ConvertToLowerCase(); + for ( DwParameter * param = headers.ContentType().FirstParameter() ; param ; param = param->Next() ) { + DwString this_attr = param->Attribute(); + this_attr.ConvertToLowerCase(); // what a braindead design! + if ( this_attr == attr ) + return QString::fromLatin1( param->Value().data(), param->Value().size() ); + // warning: misses rfc2231 handling! + } + return QString::null; +} + +KMMsgEncryptionState partNode::overallEncryptionState() const +{ + KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown; + if( mEncryptionState == KMMsgNotEncrypted ) { + // NOTE: children are tested ONLY when parent is not encrypted + if( mChild ) + myState = mChild->overallEncryptionState(); + else + myState = KMMsgNotEncrypted; + } + else { // part is partially or fully encrypted + myState = mEncryptionState; + } + // siblings are tested always + if( mNext ) { + KMMsgEncryptionState otherState = mNext->overallEncryptionState(); + switch( otherState ) { + case KMMsgEncryptionStateUnknown: + break; + case KMMsgNotEncrypted: + if( myState == KMMsgFullyEncrypted ) + myState = KMMsgPartiallyEncrypted; + else if( myState != KMMsgPartiallyEncrypted ) + myState = KMMsgNotEncrypted; + break; + case KMMsgPartiallyEncrypted: + myState = KMMsgPartiallyEncrypted; + break; + case KMMsgFullyEncrypted: + if( myState != KMMsgFullyEncrypted ) + myState = KMMsgPartiallyEncrypted; + break; + case KMMsgEncryptionProblematic: + break; + } + } + +//kdDebug(5006) << "\n\n KMMsgEncryptionState: " << myState << endl; + + return myState; +} + + +KMMsgSignatureState partNode::overallSignatureState() const +{ + KMMsgSignatureState myState = KMMsgSignatureStateUnknown; + if( mSignatureState == KMMsgNotSigned ) { + // children are tested ONLY when parent is not signed + if( mChild ) + myState = mChild->overallSignatureState(); + else + myState = KMMsgNotSigned; + } + else { // part is partially or fully signed + myState = mSignatureState; + } + // siblings are tested always + if( mNext ) { + KMMsgSignatureState otherState = mNext->overallSignatureState(); + switch( otherState ) { + case KMMsgSignatureStateUnknown: + break; + case KMMsgNotSigned: + if( myState == KMMsgFullySigned ) + myState = KMMsgPartiallySigned; + else if( myState != KMMsgPartiallySigned ) + myState = KMMsgNotSigned; + break; + case KMMsgPartiallySigned: + myState = KMMsgPartiallySigned; + break; + case KMMsgFullySigned: + if( myState != KMMsgFullySigned ) + myState = KMMsgPartiallySigned; + break; + case KMMsgEncryptionProblematic: + break; + } + } + +//kdDebug(5006) << "\n\n KMMsgSignatureState: " << myState << endl; + + return myState; +} + + +int partNode::nodeId() const +{ + int curId = 0; + partNode* rootNode = const_cast<partNode*>( this ); + while( rootNode->mRoot ) + rootNode = rootNode->mRoot; + return rootNode->calcNodeIdOrFindNode( curId, this, 0, 0 ); +} + + +partNode* partNode::findId( int id ) +{ + int curId = 0; + partNode* rootNode = this; + while( rootNode->mRoot ) + rootNode = rootNode->mRoot; + partNode* foundNode; + rootNode->calcNodeIdOrFindNode( curId, 0, id, &foundNode ); + return foundNode; +} + + +int partNode::calcNodeIdOrFindNode( int &curId, const partNode* findNode, int findId, partNode** foundNode ) +{ + // We use the same algorithm to determine the id of a node and + // to find the node when id is known. + curId++; + // check for node ? + if( findNode && this == findNode ) + return curId; + // check for id ? + if( foundNode && curId == findId ) { + *foundNode = this; + return curId; + } + if( mChild ) + { + int res = mChild->calcNodeIdOrFindNode( curId, findNode, findId, foundNode ); + if (res != -1) return res; + } + if( mNext ) + return mNext->calcNodeIdOrFindNode( curId, findNode, findId, foundNode ); + + if( foundNode ) + *foundNode = 0; + return -1; +} + + +partNode* partNode::findType( int type, int subType, bool deep, bool wide ) +{ +#ifndef NDEBUG + DwString typeStr, subTypeStr; + DwTypeEnumToStr( mType, typeStr ); + DwSubtypeEnumToStr( mSubType, subTypeStr ); + kdDebug(5006) << "partNode::findType() is looking at " << typeStr.c_str() + << "/" << subTypeStr.c_str() << endl; +#endif + if( (mType != DwMime::kTypeUnknown) + && ( (type == DwMime::kTypeUnknown) + || (type == mType) ) + && ( (subType == DwMime::kSubtypeUnknown) + || (subType == mSubType) ) ) + return this; + if ( mChild && deep ) + return mChild->findType( type, subType, deep, wide ); + if ( mNext && wide ) + return mNext->findType( type, subType, deep, wide ); + return 0; +} + +partNode* partNode::findNodeForDwPart( DwBodyPart* part ) +{ + partNode* found = 0; + if( kasciistricmp( dwPart()->partId(), part->partId() ) == 0 ) + return this; + if( mChild ) + found = mChild->findNodeForDwPart( part ); + if( mNext && !found ) + found = mNext->findNodeForDwPart( part ); + return found; +} + +partNode* partNode::findTypeNot( int type, int subType, bool deep, bool wide ) +{ + if( (mType != DwMime::kTypeUnknown) + && ( (type == DwMime::kTypeUnknown) + || (type != mType) ) + && ( (subType == DwMime::kSubtypeUnknown) + || (subType != mSubType) ) ) + return this; + if ( mChild && deep ) + return mChild->findTypeNot( type, subType, deep, wide ); + if ( mNext && wide ) + return mNext->findTypeNot( type, subType, deep, wide ); + return 0; +} + +void partNode::fillMimePartTree( KMMimePartTreeItem* parentItem, + KMMimePartTree* mimePartTree, + QString labelDescr, + QString labelCntType, + QString labelEncoding, + KIO::filesize_t size, + bool revertOrder ) +{ + if( parentItem || mimePartTree ) { + + if( mNext ) + mNext->fillMimePartTree( parentItem, mimePartTree, + QString::null, QString::null, QString::null, 0, + revertOrder ); + + QString cntDesc, cntType, cntEnc; + KIO::filesize_t cntSize = 0; + + if( labelDescr.isEmpty() ) { + DwHeaders* headers = 0; + if( mDwPart && mDwPart->hasHeaders() ) + headers = &mDwPart->Headers(); + if( headers && headers->HasSubject() ) + cntDesc = KMMsgBase::decodeRFC2047String( headers->Subject().AsString().c_str() ); + if( headers && headers->HasContentType()) { + cntType = headers->ContentType().TypeStr().c_str(); + cntType += '/'; + cntType += headers->ContentType().SubtypeStr().c_str(); + } + else + cntType = "text/plain"; + if( cntDesc.isEmpty() ) + cntDesc = msgPart().contentDescription(); + if( cntDesc.isEmpty() ) + cntDesc = msgPart().name().stripWhiteSpace(); + if( cntDesc.isEmpty() ) + cntDesc = msgPart().fileName(); + if( cntDesc.isEmpty() ) { + if( mRoot && mRoot->mRoot ) + cntDesc = i18n("internal part"); + else + cntDesc = i18n("body part"); + } + cntEnc = msgPart().contentTransferEncodingStr(); + if( mDwPart ) + cntSize = mDwPart->BodySize(); + } else { + cntDesc = labelDescr; + cntType = labelCntType; + cntEnc = labelEncoding; + cntSize = size; + } + // remove linebreak+whitespace from folded Content-Description + cntDesc.replace( QRegExp("\\n\\s*"), " " ); + +kdDebug(5006) << " Inserting one item into MimePartTree" << endl; +kdDebug(5006) << " Content-Type: " << cntType << endl; + if( parentItem ) + mMimePartTreeItem = new KMMimePartTreeItem( parentItem, + this, + cntDesc, + cntType, + cntEnc, + cntSize, + revertOrder ); + else if( mimePartTree ) + mMimePartTreeItem = new KMMimePartTreeItem( mimePartTree, + this, + cntDesc, + cntType, + cntEnc, + cntSize ); + mMimePartTreeItem->setOpen( true ); + if( mChild ) + mChild->fillMimePartTree( mMimePartTreeItem, 0, + QString::null, QString::null, QString::null, 0, + revertOrder ); + + } +} + +void partNode::adjustDefaultType( partNode* node ) +{ + // Only bodies of 'Multipart/Digest' objects have + // default type 'Message/RfC822'. All other bodies + // have default type 'Text/Plain' (khz, 5.12.2001) + if( node && DwMime::kTypeUnknown == node->type() ) { + if( node->mRoot + && DwMime::kTypeMultipart == node->mRoot->type() + && DwMime::kSubtypeDigest == node->mRoot->subType() ) { + node->setType( DwMime::kTypeMessage ); + node->setSubType( DwMime::kSubtypeRfc822 ); + } + else + { + node->setType( DwMime::kTypeText ); + node->setSubType( DwMime::kSubtypePlain ); + } + } +} + +bool partNode::isAttachment() const +{ + if( !dwPart() ) + return false; + if ( !dwPart()->hasHeaders() ) + return false; + DwHeaders& headers = dwPart()->Headers(); + if( !headers.HasContentDisposition() ) + return false; + return ( headers.ContentDisposition().DispositionType() + == DwMime::kDispTypeAttachment ); +} + +bool partNode::isHeuristicalAttachment() const { + if ( isAttachment() ) + return true; + const KMMessagePart & p = msgPart(); + return !p.fileName().isEmpty() || !p.name().isEmpty() ; +} + +partNode * partNode::next( bool allowChildren ) const { + if ( allowChildren ) + if ( partNode * c = firstChild() ) + return c; + if ( partNode * s = nextSibling() ) + return s; + for ( partNode * p = parentNode() ; p ; p = p->parentNode() ) + if ( partNode * s = p->nextSibling() ) + return s; + return 0; +} + +bool partNode::isFirstTextPart() const { + if ( type() != DwMime::kTypeText ) + return false; + const partNode * root = this; + // go up until we reach the root node of a message (of the actual message or + // of an attached message) + while ( const partNode * p = root->parentNode() ) { + if ( p->type() == DwMime::kTypeMessage ) + break; + else + root = p; + } + for ( const partNode * n = root ; n ; n = n->next() ) + if ( n->type() == DwMime::kTypeText ) + return n == this; + kdFatal() << "partNode::isFirstTextPart(): Didn't expect to end up here..." << endl; + return false; // make comiler happy +} + +bool partNode::hasContentDispositionInline() const +{ + if( !dwPart() ) + return false; + DwHeaders& headers = dwPart()->Headers(); + if( headers.HasContentDisposition() ) + return ( headers.ContentDisposition().DispositionType() + == DwMime::kDispTypeInline ); + else + return false; +} + +const QString& partNode::trueFromAddress() const +{ + const partNode* node = this; + while( node->mFromAddress.isEmpty() && node->mRoot ) + node = node->mRoot; + return node->mFromAddress; +} |