• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

KMIME Library

kmime_content.cpp
Go to the documentation of this file.
00001 /*
00002     kmime_content.cpp
00003 
00004     KMime, the KDE Internet mail/usenet news message library.
00005     Copyright (c) 2001 the KMime authors.
00006     See file AUTHORS for details
00007     Copyright (c) 2006 Volker Krause <vkrause@kde.org>
00008     Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
00009 
00010     This library is free software; you can redistribute it and/or
00011     modify it under the terms of the GNU Library General Public
00012     License as published by the Free Software Foundation; either
00013     version 2 of the License, or (at your option) any later version.
00014 
00015     This library is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018     Library General Public License for more details.
00019 
00020     You should have received a copy of the GNU Library General Public License
00021     along with this library; see the file COPYING.LIB.  If not, write to
00022     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023     Boston, MA 02110-1301, USA.
00024 */
00037 #include "kmime_content.h"
00038 #include "kmime_content_p.h"
00039 #include "kmime_codecs.h"
00040 #include "kmime_message.h"
00041 #include "kmime_header_parsing.h"
00042 #include "kmime_header_parsing_p.h"
00043 #include "kmime_parsers.h"
00044 #include "kmime_util_p.h"
00045 
00046 #include <kcharsets.h>
00047 #include <kcodecs.h>
00048 #include <kglobal.h>
00049 #include <klocale.h>
00050 #include <kdebug.h>
00051 
00052 #include <QtCore/QTextCodec>
00053 #include <QtCore/QTextStream>
00054 #include <QtCore/QByteArray>
00055 
00056 using namespace KMime;
00057 
00058 namespace KMime {
00059 
00060 Content::Content()
00061   : d_ptr( new ContentPrivate( this ) )
00062 {
00063 }
00064 
00065 Content::Content( Content *parent )
00066   : d_ptr( new ContentPrivate( this ) )
00067 {
00068   d_ptr->parent = parent;
00069 }
00070 
00071 Content::Content( const QByteArray &h, const QByteArray &b )
00072   : d_ptr( new ContentPrivate( this ) )
00073 {
00074   d_ptr->head = h;
00075   d_ptr->body = b;
00076 }
00077 
00078 Content::Content( const QByteArray &h, const QByteArray &b, Content *parent )
00079   : d_ptr( new ContentPrivate( this ) )
00080 {
00081   d_ptr->head = h;
00082   d_ptr->body = b;
00083   d_ptr->parent = parent;
00084 }
00085 
00086 Content::Content( ContentPrivate *d )
00087   : d_ptr( d )
00088 {
00089 }
00090 
00091 Content::~Content()
00092 {
00093   qDeleteAll( h_eaders );
00094   h_eaders.clear();
00095   delete d_ptr;
00096   d_ptr = 0;
00097 }
00098 
00099 bool Content::hasContent() const
00100 {
00101   return !d_ptr->head.isEmpty() || !d_ptr->body.isEmpty() || !d_ptr->contents().isEmpty();
00102 }
00103 
00104 void Content::setContent( const QList<QByteArray> &l )
00105 {
00106   Q_D(Content);
00107   //qDebug("Content::setContent( const QList<QByteArray> &l ) : start");
00108   d->head.clear();
00109   d->body.clear();
00110 
00111   //usage of textstreams is much faster than simply appending the strings
00112   QTextStream hts( &( d->head ), QIODevice::WriteOnly );
00113   QTextStream bts( &( d->body ), QIODevice::WriteOnly );
00114   hts.setCodec( "ISO 8859-1" );
00115   bts.setCodec( "ISO 8859-1" );
00116 
00117   bool isHead = true;
00118   foreach ( const QByteArray& line, l ) {
00119     if ( isHead && line.isEmpty() ) {
00120       isHead = false;
00121       continue;
00122     }
00123     if ( isHead ) {
00124       hts << line << "\n";
00125     } else {
00126       bts << line << "\n";
00127     }
00128   }
00129 
00130   //qDebug("Content::setContent( const QList<QByteArray> & l ) : finished");
00131 }
00132 
00133 void Content::setContent( const QByteArray &s )
00134 {
00135   Q_D(Content);
00136   KMime::HeaderParsing::extractHeaderAndBody( s, d->head, d->body );
00137 }
00138 
00139 QByteArray Content::head() const
00140 {
00141   return d_ptr->head;
00142 }
00143 
00144 void Content::setHead( const QByteArray &head )
00145 {
00146   d_ptr->head = head;
00147   if ( !head.endsWith( '\n' ) )
00148     d_ptr->head += '\n';
00149 }
00150 
00151 QByteArray Content::body() const
00152 {
00153   return d_ptr->body;
00154 }
00155 
00156 void Content::setBody( const QByteArray &body )
00157 {
00158   d_ptr->body = body;
00159 }
00160 
00161 void Content::parse()
00162 {
00163   Q_D( Content );
00164 
00165   // Clean up old headers and parse them again.
00166   qDeleteAll( h_eaders );
00167   h_eaders.clear();
00168   h_eaders = HeaderParsing::parseHeaders( d->head );
00169   foreach( Headers::Base *h, h_eaders ) {
00170     h->setParent( this );
00171   }
00172 
00173   // If we are frozen, save the body as-is. This is done because parsing
00174   // changes the content (it loses preambles and epilogues, converts uuencode->mime, etc.)
00175   if( d->frozen ) {
00176     d->frozenBody = d->body;
00177   }
00178 
00179   // Clean up old sub-Contents and parse them again.
00180   qDeleteAll( d->multipartContents );
00181   d->multipartContents.clear();
00182   d->clearBodyMessage();
00183   Headers::ContentType *ct = contentType();
00184   if( ct->isText() ) {
00185     // This content is either text, or of unknown type.
00186 
00187     if( d->parseUuencoded() ) {
00188       // This is actually uuencoded content generated by broken software.
00189     } else if( d->parseYenc() ) {
00190       // This is actually yenc content generated by broken software.
00191     } else {
00192       // This is just plain text.
00193     }
00194   } else if( ct->isMultipart() ) {
00195     // This content claims to be MIME multipart.
00196 
00197     if( d->parseMultipart() ) {
00198       // This is actual MIME multipart content.
00199     } else {
00200       // Parsing failed; treat this content as "text/plain".
00201       ct->setMimeType( "text/plain" );
00202       ct->setCharset( "US-ASCII" );
00203     }
00204   } else {
00205     // This content is something else, like an encapsulated message or a binary attachment
00206     // or something like that
00207     if ( bodyIsMessage() ) {
00208       d->bodyAsMessage = Message::Ptr( new Message );
00209       d->bodyAsMessage->setContent( d->body );
00210       d->bodyAsMessage->setFrozen( d->frozen );
00211       d->bodyAsMessage->parse();
00212       d->bodyAsMessage->d_ptr->parent = this;
00213 
00214       // Clear the body, as it is now represented by d->bodyAsMessage. This is the same behavior
00215       // as with multipart contents, since parseMultipart() clears the body as well
00216       d->body.clear();
00217     }
00218   }
00219 }
00220 
00221 bool Content::isFrozen() const
00222 {
00223   return d_ptr->frozen;
00224 }
00225 
00226 void Content::setFrozen( bool frozen )
00227 {
00228   d_ptr->frozen = frozen;
00229 }
00230 
00231 void Content::assemble()
00232 {
00233   Q_D( Content );
00234   if( d->frozen ) {
00235     return;
00236   }
00237 
00238   d->head = assembleHeaders();
00239   foreach( Content *c, contents() ) {
00240     c->assemble();
00241   }
00242 }
00243 
00244 QByteArray Content::assembleHeaders()
00245 {
00246   QByteArray newHead;
00247   foreach( const Headers::Base *h, h_eaders ) {
00248     if( !h->isEmpty() ) {
00249       newHead += h->as7BitString() + '\n';
00250     }
00251   }
00252 
00253   return newHead;
00254 }
00255 
00256 void Content::clear()
00257 {
00258   Q_D(Content);
00259   qDeleteAll( h_eaders );
00260   h_eaders.clear();
00261   clearContents();
00262   d->head.clear();
00263   d->body.clear();
00264 }
00265 
00266 void Content::clearContents( bool del )
00267 {
00268   Q_D(Content);
00269   if( del ) {
00270     qDeleteAll( d->multipartContents );
00271   }
00272   d->multipartContents.clear();
00273   d->clearBodyMessage();
00274 }
00275 
00276 QByteArray Content::encodedContent( bool useCrLf )
00277 {
00278   Q_D(Content);
00279   QByteArray e;
00280 
00281   // Head.
00282   e = d->head;
00283   e += '\n';
00284   e += encodedBody();
00285 
00286   if ( useCrLf ) {
00287     return LFtoCRLF( e );
00288   } else {
00289     return e;
00290   }
00291 }
00292 
00293 QByteArray Content::encodedBody()
00294 {
00295   Q_D( Content );
00296   QByteArray e;
00297   // Body.
00298   if( d->frozen ) {
00299     // This Content is frozen.
00300     if( d->frozenBody.isEmpty() ) {
00301       // This Content has never been parsed.
00302       e += d->body;
00303     } else {
00304       // Use the body as it was before parsing.
00305       e += d->frozenBody;
00306     }
00307   } else if( bodyIsMessage() && d->bodyAsMessage ) {
00308     // This is an encapsulated message
00309     // No encoding needed, as the ContentTransferEncoding can only be 7bit
00310     // for encapsulated messages
00311     e += d->bodyAsMessage->encodedContent();
00312   } else if( !d->body.isEmpty() ) {
00313     // This is a single-part Content.
00314     Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00315 
00316     if ( enc->needToEncode() ) {
00317       if ( enc->encoding() == Headers::CEquPr ) {
00318         e += KCodecs::quotedPrintableEncode( d->body, false );
00319       } else {
00320         e += KCodecs::base64Encode( d->body, true );
00321         e += '\n';
00322       }
00323     } else {
00324       e += d->body;
00325     }
00326   }
00327 
00328   if ( !d->frozen && !d->multipartContents.isEmpty() ) {
00329     // This is a multipart Content.
00330     Headers::ContentType *ct=contentType();
00331     QByteArray boundary = "\n--" + ct->boundary();
00332 
00333     if ( !d->preamble.isEmpty() )
00334       e += d->preamble;
00335 
00336     //add all (encoded) contents separated by boundaries
00337     foreach ( Content *c, d->multipartContents ) {
00338       e+=boundary + '\n';
00339       e += c->encodedContent( false );  // don't convert LFs here, we do that later!!!!!
00340     }
00341     //finally append the closing boundary
00342     e += boundary+"--\n";
00343 
00344     if ( !d->epilogue.isEmpty() )
00345       e += d->epilogue;
00346   };
00347   return e;
00348 }
00349 
00350 QByteArray Content::decodedContent()
00351 {
00352   QByteArray ret;
00353   Headers::ContentTransferEncoding *ec=contentTransferEncoding();
00354   bool removeTrailingNewline=false;
00355 
00356   if ( d_ptr->body.length() == 0 ) {
00357     return ret;
00358   }
00359 
00360   if ( ec->decoded() ) {
00361     ret = d_ptr->body;
00362     removeTrailingNewline = true;
00363   } else {
00364     switch( ec->encoding() ) {
00365     case Headers::CEbase64 :
00366     {
00367       KMime::Codec *codec = KMime::Codec::codecForName( "base64" );
00368       Q_ASSERT( codec );
00369       ret.resize( codec->maxDecodedSizeFor( d_ptr->body.size() ) );
00370       KMime::Decoder* decoder = codec->makeDecoder();
00371       QByteArray::const_iterator inputIt = d_ptr->body.constBegin();
00372       QByteArray::iterator resultIt = ret.begin();
00373       decoder->decode( inputIt, d_ptr->body.constEnd(), resultIt, ret.end() );
00374       ret.truncate( resultIt - ret.begin() );
00375       break;
00376     }
00377     case Headers::CEquPr :
00378       ret = KCodecs::quotedPrintableDecode( d_ptr->body );
00379       removeTrailingNewline = true;
00380       break;
00381     case Headers::CEuuenc :
00382       KCodecs::uudecode( d_ptr->body, ret );
00383       break;
00384     case Headers::CEbinary :
00385       ret = d_ptr->body;
00386       removeTrailingNewline = false;
00387       break;
00388     default :
00389       ret = d_ptr->body;
00390       removeTrailingNewline = true;
00391     }
00392   }
00393 
00394   if ( removeTrailingNewline && ( ret.size() > 0 ) && ( ret[ret.size()-1] == '\n') ) {
00395     ret.resize( ret.size() - 1 );
00396   }
00397 
00398   return ret;
00399 }
00400 
00401 QString Content::decodedText( bool trimText, bool removeTrailingNewlines )
00402 {
00403   if ( !decodeText() ) { //this is not a text content !!
00404     return QString();
00405   }
00406 
00407   bool ok = true;
00408   QTextCodec *codec =
00409     KGlobal::charsets()->codecForName( QLatin1String( contentType()->charset() ), ok );
00410 
00411   QString s = codec->toUnicode( d_ptr->body.data(), d_ptr->body.length() );
00412 
00413   if ( trimText || removeTrailingNewlines ) {
00414     int i;
00415     for ( i = s.length() - 1; i >= 0; --i ) {
00416       if ( trimText ) {
00417         if ( !s[i].isSpace() ) {
00418           break;
00419         }
00420       }
00421       else {
00422         if ( s[i] != QLatin1Char( '\n' ) ) {
00423           break;
00424         }
00425       }
00426     }
00427     s.truncate( i + 1 );
00428   } else {
00429     if ( s.right( 1 ) == QLatin1String( "\n" ) ) {
00430       s.truncate( s.length() - 1 ); // remove trailing new-line
00431     }
00432   }
00433 
00434   return s;
00435 }
00436 
00437 void Content::fromUnicodeString( const QString &s )
00438 {
00439   bool ok = true;
00440   QTextCodec *codec =
00441     KGlobal::charsets()->codecForName( QLatin1String( contentType()->charset() ), ok );
00442 
00443   if ( !ok ) { // no suitable codec found => try local settings and hope the best ;-)
00444     codec = KGlobal::locale()->codecForEncoding();
00445     QByteArray chset = KGlobal::locale()->encoding();
00446     contentType()->setCharset( chset );
00447   }
00448 
00449   d_ptr->body = codec->fromUnicode( s );
00450   contentTransferEncoding()->setDecoded( true ); //text is always decoded
00451 }
00452 
00453 Content *Content::textContent()
00454 {
00455   Content *ret=0;
00456 
00457   //return the first content with mimetype=text/*
00458   if ( contentType()->isText() ) {
00459     ret = this;
00460   } else {
00461     foreach ( Content *c, d_ptr->contents() ) {
00462       if ( ( ret = c->textContent() ) != 0 ) {
00463         break;
00464       }
00465     }
00466   }
00467   return ret;
00468 }
00469 
00470 Content::List Content::attachments( bool incAlternatives )
00471 {
00472   List attachments;
00473   if ( d_ptr->contents().isEmpty() ) {
00474     attachments.append( this );
00475   } else {
00476     foreach ( Content *c, d_ptr->contents() ) {
00477       if ( !incAlternatives &&
00478            c->contentType()->category() == Headers::CCalternativePart ) {
00479         continue;
00480       } else {
00481         attachments += c->attachments( incAlternatives );
00482       }
00483     }
00484   }
00485 
00486   if ( isTopLevel() ) {
00487     Content *text = textContent();
00488     if ( text ) {
00489       attachments.removeAll( text );
00490     }
00491   }
00492   return attachments;
00493 }
00494 
00495 Content::List Content::contents() const
00496 {
00497   return d_ptr->contents();
00498 }
00499 
00500 void Content::addContent( Content *c, bool prepend )
00501 {
00502   Q_D( Content );
00503 
00504   // This method makes no sense for encapsulated messages
00505   Q_ASSERT( !bodyIsMessage() );
00506 
00507   // If this message is single-part; make it multipart first.
00508   if( d->multipartContents.isEmpty() && !contentType()->isMultipart() ) {
00509     // The current body will be our first sub-Content.
00510     Content *main = new Content( this );
00511 
00512     // Move the MIME headers to the newly created sub-Content.
00513     // NOTE: The other headers (RFC5322 headers like From:, To:, as well as X-headers
00514     // are not moved to the subcontent; they remain with the top-level content.
00515     for ( Headers::Base::List::iterator it = h_eaders.begin();
00516           it != h_eaders.end(); ) {
00517       if ( (*it)->isMimeHeader() ) {
00518         // Add to new content.
00519         main->setHeader( *it );
00520         // Remove from this content.
00521         it = h_eaders.erase( it );
00522       } else {
00523         ++it;
00524       }
00525     }
00526 
00527     // Adjust the Content-Type of the newly created sub-Content.
00528     main->contentType()->setCategory( Headers::CCmixedPart );
00529 
00530     // Move the body to the new subcontent.
00531     main->setBody( d->body );
00532     d->body.clear();
00533 
00534     // Add the subcontent.
00535     d->multipartContents.append( main );
00536 
00537     // Convert this content to "multipart/mixed".
00538     Headers::ContentType *ct = contentType();
00539     ct->setMimeType( "multipart/mixed" );
00540     ct->setBoundary( multiPartBoundary() );
00541     ct->setCategory( Headers::CCcontainer );
00542     contentTransferEncoding()->clear();  // 7Bit, decoded.
00543   }
00544 
00545   // Add the new content.
00546   if( prepend ) {
00547     d->multipartContents.prepend( c );
00548   } else {
00549     d->multipartContents.append( c );
00550   }
00551 
00552   if( c->parent() != this ) {
00553     // If the content was part of something else, this will remove it from there.
00554     c->setParent( this );
00555   }
00556 }
00557 
00558 void Content::removeContent( Content *c, bool del )
00559 {
00560   Q_D( Content );
00561   Q_ASSERT( d->multipartContents.contains( c ) );
00562 
00563   // This method makes no sense for encapsulated messages. Should be covered by the above
00564   // assert already, though.
00565   Q_ASSERT( !bodyIsMessage() );
00566 
00567   d->multipartContents.removeAll( c );
00568   if ( del ) {
00569     delete c;
00570   } else {
00571     c->d_ptr->parent = 0;
00572   }
00573 
00574   // If only one content is left, turn this content into a single-part.
00575   if( d->multipartContents.count() == 1 ) {
00576     Content *main = d->multipartContents.first();
00577 
00578     // Move all headers from the old subcontent to ourselves.
00579     // NOTE: This also sets the new Content-Type.
00580     foreach( Headers::Base *h, main->h_eaders ) {
00581       setHeader( h ); // Will remove the old one if present.
00582     }
00583     main->h_eaders.clear();
00584 
00585     // Move the body.
00586     d->body = main->body();
00587 
00588     // Delete the old subcontent.
00589     delete main;
00590     d->multipartContents.clear();
00591   }
00592 }
00593 
00594 void Content::changeEncoding( Headers::contentEncoding e )
00595 {
00596   // This method makes no sense for encapsulated messages, they are always 7bit
00597   // encoded.
00598   Q_ASSERT( !bodyIsMessage() );
00599 
00600   Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00601   if( enc->encoding() == e ) {
00602     // Nothing to do.
00603     return;
00604   }
00605 
00606   if( decodeText() ) {
00607     // This is textual content.  Textual content is stored decoded.
00608     Q_ASSERT( enc->decoded() );
00609     enc->setEncoding( e );
00610   } else {
00611     // This is non-textual content.  Re-encode it.
00612     if( e == Headers::CEbase64 ) {
00613       d_ptr->body = KCodecs::base64Encode( decodedContent(), true );
00614       d_ptr->body.append( "\n" );
00615       enc->setEncoding( e );
00616       enc->setDecoded( false );
00617     } else {
00618       // It only makes sense to convert binary stuff to base64.
00619       Q_ASSERT( false );
00620     }
00621   }
00622 }
00623 
00624 void Content::toStream( QTextStream &ts, bool scrambleFromLines )
00625 {
00626   QByteArray ret = encodedContent( false );
00627 
00628   if ( scrambleFromLines ) {
00629     // FIXME Why are only From lines with a preceding empty line considered?
00630     //       And, of course, all lines starting with >*From have to be escaped
00631     //       because otherwise the transformation is not revertable.
00632     ret.replace( "\n\nFrom ", "\n\n>From ");
00633   }
00634   ts << ret;
00635 }
00636 
00637 Headers::Generic *Content::getNextHeader( QByteArray &head )
00638 {
00639   return d_ptr->nextHeader( head );
00640 }
00641 
00642 Headers::Generic *Content::nextHeader( QByteArray &head )
00643 {
00644   return d_ptr->nextHeader( head );
00645 }
00646 
00647 Headers::Generic *ContentPrivate::nextHeader( QByteArray &_head )
00648 {
00649   Headers::Base *header = HeaderParsing::extractFirstHeader( _head );
00650   if ( !header ) {
00651     return 0;
00652   }
00653   // Convert it from the real class to Generic.
00654   Headers::Generic *ret = new Headers::Generic( header->type(), q_ptr );
00655   ret->from7BitString( header->as7BitString() );
00656   return ret;
00657 }
00658 
00659 Headers::Base *Content::getHeaderByType( const char *type )
00660 {
00661   return headerByType( type );
00662 }
00663 
00664 Headers::Base *Content::headerByType( const char *type )
00665 {
00666   Q_ASSERT( type  && *type );
00667 
00668   foreach( Headers::Base *h, h_eaders ) {
00669     if( h->is( type ) ) {
00670       return h; // Found.
00671     }
00672   }
00673 
00674   return 0; // Not found.
00675 }
00676 
00677 Headers::Base::List Content::headersByType( const char *type )
00678 {
00679   Q_ASSERT( type && *type );
00680 
00681   Headers::Base::List result;
00682 
00683   foreach( Headers::Base *h, h_eaders ) {
00684     if( h->is( type ) ) {
00685       result << h;
00686     }
00687   }
00688 
00689   return result;
00690 }
00691 
00692 void Content::setHeader( Headers::Base *h )
00693 {
00694   Q_ASSERT( h );
00695   removeHeader( h->type() );
00696   appendHeader( h );
00697 }
00698 
00699 void Content::appendHeader( Headers::Base *h )
00700 {
00701   h_eaders.append( h );
00702   h->setParent( this );
00703 }
00704 
00705 void Content::prependHeader( Headers::Base *h )
00706 {
00707   h_eaders.prepend( h );
00708   h->setParent( this );
00709 }
00710 
00711 bool Content::removeHeader( const char *type )
00712 {
00713   for ( Headers::Base::List::iterator it = h_eaders.begin();
00714         it != h_eaders.end(); ++it )
00715     if ( (*it)->is(type) ) {
00716       delete (*it);
00717       h_eaders.erase( it );
00718       return true;
00719     }
00720 
00721   return false;
00722 }
00723 
00724 bool Content::hasHeader( const char *type )
00725 {
00726   return headerByType( type ) != 0;
00727 }
00728 
00729 int Content::size()
00730 {
00731   int ret = d_ptr->body.length();
00732 
00733   if ( contentTransferEncoding()->encoding() == Headers::CEbase64 ) {
00734     return ret * 3 / 4; //base64 => 6 bit per byte
00735   }
00736 
00737   // Not handling quoted-printable here since that requires actually
00738   // converting the content, and that is O(size_of_content).
00739   // For quoted-printable, this is only an approximate size.
00740 
00741   return ret;
00742 }
00743 
00744 int Content::storageSize() const
00745 {
00746   const Q_D(Content);
00747   int s = d->head.size();
00748 
00749   if ( d->contents().isEmpty() ) {
00750     s += d->body.size();
00751   } else {
00752 
00753     // FIXME: This should take into account the boundary headers that are added in
00754     //        encodedContent!
00755     foreach ( Content *c, d->contents() ) {
00756       s += c->storageSize();
00757     }
00758   }
00759 
00760   return s;
00761 }
00762 
00763 int Content::lineCount() const
00764 {
00765   const Q_D(Content);
00766   int ret = 0;
00767   if ( !isTopLevel() ) {
00768     ret += d->head.count( '\n' );
00769   }
00770   ret += d->body.count( '\n' );
00771 
00772   foreach ( Content *c, d->contents() ) {
00773     ret += c->lineCount();
00774   }
00775 
00776   return ret;
00777 }
00778 
00779 QByteArray Content::rawHeader( const char *name ) const
00780 {
00781   return KMime::extractHeader( d_ptr->head, name );
00782 }
00783 
00784 QList<QByteArray> Content::rawHeaders( const char *name ) const
00785 {
00786   return KMime::extractHeaders( d_ptr->head, name );
00787 }
00788 
00789 bool Content::decodeText()
00790 {
00791   Q_D(Content);
00792   Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00793 
00794   if ( !contentType()->isText() ) {
00795     return false; //non textual data cannot be decoded here => use decodedContent() instead
00796   }
00797   if ( enc->decoded() ) {
00798     return true; //nothing to do
00799   }
00800 
00801   switch( enc->encoding() )
00802   {
00803   case Headers::CEbase64 :
00804     d->body = KCodecs::base64Decode( d->body );
00805     d->body.append( "\n" );
00806     break;
00807   case Headers::CEquPr :
00808     d->body = KCodecs::quotedPrintableDecode( d->body );
00809     break;
00810   case Headers::CEuuenc :
00811     d->body = KCodecs::uudecode( d->body );
00812     d->body.append( "\n" );
00813     break;
00814   case Headers::CEbinary :
00815     // nothing to decode
00816     d->body.append( "\n" );
00817   default :
00818     break;
00819   }
00820 
00821   enc->setDecoded( true );
00822   return true;
00823 }
00824 
00825 QByteArray Content::defaultCharset() const
00826 {
00827   return d_ptr->defaultCS;
00828 }
00829 
00830 void Content::setDefaultCharset( const QByteArray &cs )
00831 {
00832   d_ptr->defaultCS = KMime::cachedCharset( cs );
00833 
00834   foreach ( Content *c, d_ptr->contents() ) {
00835     c->setDefaultCharset( cs );
00836   }
00837 
00838   // reparse the part and its sub-parts in order
00839   // to clear cached header values
00840   parse();
00841 }
00842 
00843 bool Content::forceDefaultCharset() const
00844 {
00845   return d_ptr->forceDefaultCS;
00846 }
00847 
00848 void Content::setForceDefaultCharset( bool b )
00849 {
00850   d_ptr->forceDefaultCS = b;
00851 
00852   foreach ( Content *c, d_ptr->contents() ) {
00853     c->setForceDefaultCharset( b );
00854   }
00855 
00856   // reparse the part and its sub-parts in order
00857   // to clear cached header values
00858   parse();
00859 }
00860 
00861 Content * KMime::Content::content( const ContentIndex &index ) const
00862 {
00863   if ( !index.isValid() ) {
00864     return const_cast<KMime::Content*>( this );
00865   }
00866   ContentIndex idx = index;
00867   unsigned int i = idx.pop() - 1; // one-based -> zero-based index
00868   if ( i < (unsigned int)d_ptr->contents().size() ) {
00869     return d_ptr->contents()[i]->content( idx );
00870   } else {
00871     return 0;
00872   }
00873 }
00874 
00875 ContentIndex KMime::Content::indexForContent( Content * content ) const
00876 {
00877   int i = d_ptr->contents().indexOf( content );
00878   if ( i >= 0 ) {
00879     ContentIndex ci;
00880     ci.push( i + 1 ); // zero-based -> one-based index
00881     return ci;
00882   }
00883   // not found, we need to search recursively
00884   for ( int i = 0; i < d_ptr->contents().size(); ++i ) {
00885     ContentIndex ci = d_ptr->contents()[i]->indexForContent( content );
00886     if ( ci.isValid() ) {
00887       // found it
00888       ci.push( i + 1 ); // zero-based -> one-based index
00889       return ci;
00890     }
00891   }
00892   return ContentIndex(); // not found
00893 }
00894 
00895 bool Content::isTopLevel() const
00896 {
00897   return d_ptr->parent == 0;
00898 }
00899 
00900 void Content::setParent( Content* parent )
00901 {
00902   //make sure the Content is only in the contents list of one parent object
00903   Content *oldParent = d_ptr->parent;
00904   if ( oldParent && oldParent->contents().contains( this ) ) {
00905     oldParent->removeContent( this );
00906   }
00907 
00908   d_ptr->parent = parent;
00909   if ( parent && !parent->contents().contains( this ) ) {
00910     parent->addContent( this );
00911   }
00912 }
00913 
00914 Content* Content::parent() const
00915 {
00916   return d_ptr->parent;
00917 }
00918 
00919 Content* Content::topLevel() const
00920 {
00921   Content *top = const_cast<Content*>(this);
00922   Content *c = parent();
00923   while ( c ) {
00924     top = c;
00925     c = c->parent();
00926   }
00927 
00928   return top;
00929 }
00930 
00931 ContentIndex Content::index() const
00932 {
00933   Content* top = topLevel();
00934   if ( top ) {
00935     return top->indexForContent( const_cast<Content*>(this) );
00936   }
00937 
00938   return indexForContent( const_cast<Content*>(this)  );
00939 }
00940 
00941 Message::Ptr Content::bodyAsMessage() const
00942 {
00943   if ( bodyIsMessage() && d_ptr->bodyAsMessage ) {
00944     return d_ptr->bodyAsMessage;
00945   } else {
00946     return Message::Ptr();
00947   }
00948 }
00949 
00950 bool Content::bodyIsMessage() const
00951 {
00952   // Use const_case here to work around API issue that neither header() nor hasHeader() are
00953   // const, even though they should be
00954   return const_cast<Content*>( this )->header<Headers::ContentType>( false ) &&
00955          const_cast<Content*>( this )->header<Headers::ContentType>( true )
00956                  ->mimeType().toLower() == "message/rfc822";
00957 }
00958 
00959 // @cond PRIVATE
00960 #define kmime_mk_header_accessor( type, method ) \
00961 Headers::type *Content::method( bool create ) { \
00962   return header<Headers::type>( create ); \
00963 }
00964 
00965 kmime_mk_header_accessor( ContentType, contentType )
00966 kmime_mk_header_accessor( ContentTransferEncoding, contentTransferEncoding )
00967 kmime_mk_header_accessor( ContentDisposition, contentDisposition )
00968 kmime_mk_header_accessor( ContentDescription, contentDescription )
00969 kmime_mk_header_accessor( ContentLocation, contentLocation )
00970 kmime_mk_header_accessor( ContentID, contentID )
00971 
00972 #undef kmime_mk_header_accessor
00973 // @endcond
00974 
00975 
00976 void ContentPrivate::clearBodyMessage()
00977 {
00978   bodyAsMessage.reset();
00979 }
00980 
00981 Content::List ContentPrivate::contents() const
00982 {
00983   Q_ASSERT( multipartContents.isEmpty() || !bodyAsMessage );
00984   if ( bodyAsMessage )
00985     return Content::List() << bodyAsMessage.get();
00986   else
00987     return multipartContents;
00988 }
00989 
00990 bool ContentPrivate::parseUuencoded()
00991 {
00992   Q_Q( Content );
00993   Parser::UUEncoded uup( body, KMime::extractHeader( head, "Subject" ) );
00994   if( !uup.parse() ) {
00995     return false; // Parsing failed.
00996   }
00997 
00998   Headers::ContentType *ct = q->contentType();
00999   ct->clear();
01000 
01001   if( uup.isPartial() ) {
01002     // This seems to be only a part of the message, so we treat it as "message/partial".
01003     ct->setMimeType( "message/partial" );
01004     //ct->setId( uniqueString() ); not needed yet
01005     ct->setPartialParams( uup.partialCount(), uup.partialNumber() );
01006     q->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01007   } else {
01008     // This is a complete message, so treat it as "multipart/mixed".
01009     body.clear();
01010     ct->setMimeType( "multipart/mixed" );
01011     ct->setBoundary( multiPartBoundary() );
01012     ct->setCategory( Headers::CCcontainer );
01013     q->contentTransferEncoding()->clear(); // 7Bit, decoded.
01014 
01015     // Add the plain text part first.
01016     Q_ASSERT( multipartContents.count() == 0 );
01017     {
01018       Content *c = new Content( q );
01019       c->contentType()->setMimeType( "text/plain" );
01020       c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01021       c->setBody( uup.textPart() );
01022       multipartContents.append( c );
01023     }
01024 
01025     // Now add each of the binary parts as sub-Contents.
01026     for( int i = 0; i < uup.binaryParts().count(); ++i ) {
01027       Content *c = new Content( q );
01028       c->contentType()->setMimeType( uup.mimeTypes().at( i ) );
01029       c->contentType()->setName( QLatin1String( uup.filenames().at( i ) ), QByteArray( /*charset*/ ) );
01030       c->contentTransferEncoding()->setEncoding( Headers::CEuuenc );
01031       c->contentTransferEncoding()->setDecoded( false );
01032       c->contentDisposition()->setDisposition( Headers::CDattachment );
01033       c->contentDisposition()->setFilename( QLatin1String( uup.filenames().at( i ) ) );
01034       c->setBody( uup.binaryParts().at( i ) );
01035       c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01036       multipartContents.append( c );
01037     }
01038   }
01039 
01040   return true; // Parsing successful.
01041 }
01042 
01043 bool ContentPrivate::parseYenc()
01044 {
01045   Q_Q( Content );
01046   Parser::YENCEncoded yenc( body );
01047   if( !yenc.parse() ) {
01048     return false; // Parsing failed.
01049   }
01050 
01051   Headers::ContentType *ct = q->contentType();
01052   ct->clear();
01053 
01054   if( yenc.isPartial() ) {
01055     // Assume there is exactly one decoded part.  Treat this as "message/partial".
01056     ct->setMimeType( "message/partial" );
01057     //ct->setId( uniqueString() ); not needed yet
01058     ct->setPartialParams( yenc.partialCount(), yenc.partialNumber() );
01059     q->contentTransferEncoding()->setEncoding( Headers::CEbinary );
01060     q->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01061   } else {
01062     // This is a complete message, so treat it as "multipart/mixed".
01063     body.clear();
01064     ct->setMimeType( "multipart/mixed" );
01065     ct->setBoundary( multiPartBoundary() );
01066     ct->setCategory( Headers::CCcontainer );
01067     q->contentTransferEncoding()->clear(); // 7Bit, decoded.
01068 
01069     // Add the plain text part first.
01070     Q_ASSERT( multipartContents.count() == 0 );
01071     {
01072       Content *c = new Content( q );
01073       c->contentType()->setMimeType( "text/plain" );
01074       c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01075       c->setBody( yenc.textPart() );
01076       multipartContents.append( c );
01077     }
01078 
01079     // Now add each of the binary parts as sub-Contents.
01080     for ( int i=0; i<yenc.binaryParts().count(); i++ ) {
01081       Content *c = new Content( q );
01082       c->contentType()->setMimeType( yenc.mimeTypes().at( i ) );
01083       c->contentType()->setName( QLatin1String( yenc.filenames().at( i ) ), QByteArray( /*charset*/ ) );
01084       c->contentTransferEncoding()->setEncoding( Headers::CEbinary );
01085       c->contentDisposition()->setDisposition( Headers::CDattachment );
01086       c->contentDisposition()->setFilename( QLatin1String( yenc.filenames().at( i ) ) );
01087       c->setBody( yenc.binaryParts().at( i ) ); // Yenc bodies are binary.
01088       c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01089       multipartContents.append( c );
01090     }
01091   }
01092 
01093   return true; // Parsing successful.
01094 }
01095 
01096 bool ContentPrivate::parseMultipart()
01097 {
01098   Q_Q( Content );
01099   const Headers::ContentType *ct = q->contentType();
01100   const QByteArray boundary = ct->boundary();
01101   if( boundary.isEmpty() ) {
01102     return false; // Parsing failed; invalid multipart content.
01103   }
01104   Parser::MultiPart mpp( body, boundary );
01105   if( !mpp.parse() ) {
01106     return false; // Parsing failed.
01107   }
01108 
01109   preamble = mpp.preamble();
01110   epilogue = mpp.epilouge();
01111 
01112   // Determine the category of the subparts (used in attachments()).
01113   Headers::contentCategory cat;
01114   if( ct->isSubtype( "alternative" ) ) {
01115     cat = Headers::CCalternativePart;
01116   } else {
01117     cat = Headers::CCmixedPart; // Default to "mixed".
01118   }
01119 
01120   // Create a sub-Content for every part.
01121   Q_ASSERT( multipartContents.isEmpty() );
01122   body.clear();
01123   QList<QByteArray> parts = mpp.parts();
01124   foreach( const QByteArray &part, mpp.parts() ) {
01125     Content *c = new Content( q );
01126     c->setContent( part );
01127     c->setFrozen( frozen );
01128     c->parse();
01129     c->contentType()->setCategory( cat );
01130     multipartContents.append( c );
01131   }
01132 
01133   return true; // Parsing successful.
01134 }
01135 
01136 } // namespace KMime

KMIME Library

Skip menu "KMIME Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal