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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:51 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003