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