• Skip to content
  • Skip to link menu
KDE 4.7 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   if ( !ok  || codec == NULL ) { // no suitable codec found => try local settings and hope the best ;-)
00411     codec = KGlobal::locale()->codecForEncoding();
00412     QByteArray chset = KGlobal::locale()->encoding();
00413     contentType()->setCharset( chset );
00414   }
00415   
00416   QString s = codec->toUnicode( d_ptr->body.data(), d_ptr->body.length() );
00417 
00418   if ( trimText || removeTrailingNewlines ) {
00419     int i;
00420     for ( i = s.length() - 1; i >= 0; --i ) {
00421       if ( trimText ) {
00422         if ( !s[i].isSpace() ) {
00423           break;
00424         }
00425       }
00426       else {
00427         if ( s[i] != QLatin1Char( '\n' ) ) {
00428           break;
00429         }
00430       }
00431     }
00432     s.truncate( i + 1 );
00433   } else {
00434     if ( s.right( 1 ) == QLatin1String( "\n" ) ) {
00435       s.truncate( s.length() - 1 ); // remove trailing new-line
00436     }
00437   }
00438 
00439   return s;
00440 }
00441 
00442 void Content::fromUnicodeString( const QString &s )
00443 {
00444   bool ok = true;
00445   QTextCodec *codec =
00446     KGlobal::charsets()->codecForName( QLatin1String( contentType()->charset() ), ok );
00447 
00448   if ( !ok ) { // no suitable codec found => try local settings and hope the best ;-)
00449     codec = KGlobal::locale()->codecForEncoding();
00450     QByteArray chset = KGlobal::locale()->encoding();
00451     contentType()->setCharset( chset );
00452   }
00453 
00454   d_ptr->body = codec->fromUnicode( s );
00455   contentTransferEncoding()->setDecoded( true ); //text is always decoded
00456 }
00457 
00458 Content *Content::textContent()
00459 {
00460   Content *ret=0;
00461 
00462   //return the first content with mimetype=text/*
00463   if ( contentType()->isText() ) {
00464     ret = this;
00465   } else {
00466     foreach ( Content *c, d_ptr->contents() ) {
00467       if ( ( ret = c->textContent() ) != 0 ) {
00468         break;
00469       }
00470     }
00471   }
00472   return ret;
00473 }
00474 
00475 Content::List Content::attachments( bool incAlternatives )
00476 {
00477   List attachments;
00478   if ( d_ptr->contents().isEmpty() ) {
00479     attachments.append( this );
00480   } else {
00481     foreach ( Content *c, d_ptr->contents() ) {
00482       if ( !incAlternatives &&
00483            c->contentType()->category() == Headers::CCalternativePart ) {
00484         continue;
00485       } else {
00486         attachments += c->attachments( incAlternatives );
00487       }
00488     }
00489   }
00490 
00491   if ( isTopLevel() ) {
00492     Content *text = textContent();
00493     if ( text ) {
00494       attachments.removeAll( text );
00495     }
00496   }
00497   return attachments;
00498 }
00499 
00500 Content::List Content::contents() const
00501 {
00502   return d_ptr->contents();
00503 }
00504 
00505 void Content::addContent( Content *c, bool prepend )
00506 {
00507   Q_D( Content );
00508 
00509   // This method makes no sense for encapsulated messages
00510   Q_ASSERT( !bodyIsMessage() );
00511 
00512   // If this message is single-part; make it multipart first.
00513   if( d->multipartContents.isEmpty() && !contentType()->isMultipart() ) {
00514     // The current body will be our first sub-Content.
00515     Content *main = new Content( this );
00516 
00517     // Move the MIME headers to the newly created sub-Content.
00518     // NOTE: The other headers (RFC5322 headers like From:, To:, as well as X-headers
00519     // are not moved to the subcontent; they remain with the top-level content.
00520     for ( Headers::Base::List::iterator it = h_eaders.begin();
00521           it != h_eaders.end(); ) {
00522       if ( (*it)->isMimeHeader() ) {
00523         // Add to new content.
00524         main->setHeader( *it );
00525         // Remove from this content.
00526         it = h_eaders.erase( it );
00527       } else {
00528         ++it;
00529       }
00530     }
00531 
00532     // Adjust the Content-Type of the newly created sub-Content.
00533     main->contentType()->setCategory( Headers::CCmixedPart );
00534 
00535     // Move the body to the new subcontent.
00536     main->setBody( d->body );
00537     d->body.clear();
00538 
00539     // Add the subcontent.
00540     d->multipartContents.append( main );
00541 
00542     // Convert this content to "multipart/mixed".
00543     Headers::ContentType *ct = contentType();
00544     ct->setMimeType( "multipart/mixed" );
00545     ct->setBoundary( multiPartBoundary() );
00546     ct->setCategory( Headers::CCcontainer );
00547     contentTransferEncoding()->clear();  // 7Bit, decoded.
00548   }
00549 
00550   // Add the new content.
00551   if( prepend ) {
00552     d->multipartContents.prepend( c );
00553   } else {
00554     d->multipartContents.append( c );
00555   }
00556 
00557   if( c->parent() != this ) {
00558     // If the content was part of something else, this will remove it from there.
00559     c->setParent( this );
00560   }
00561 }
00562 
00563 void Content::removeContent( Content *c, bool del )
00564 {
00565   Q_D( Content );
00566   if( !d->multipartContents.contains( c ) )
00567     return;
00568   // This method makes no sense for encapsulated messages. Should be covered by the above
00569   // assert already, though.
00570   Q_ASSERT( !bodyIsMessage() );
00571 
00572   d->multipartContents.removeAll( c );
00573   if ( del ) {
00574     delete c;
00575   } else {
00576     c->d_ptr->parent = 0;
00577   }
00578 
00579   // If only one content is left, turn this content into a single-part.
00580   if( d->multipartContents.count() == 1 ) {
00581     Content *main = d->multipartContents.first();
00582 
00583     // Move all headers from the old subcontent to ourselves.
00584     // NOTE: This also sets the new Content-Type.
00585     foreach( Headers::Base *h, main->h_eaders ) {
00586       setHeader( h ); // Will remove the old one if present.
00587     }
00588     main->h_eaders.clear();
00589 
00590     // Move the body.
00591     d->body = main->body();
00592 
00593     // Delete the old subcontent.
00594     delete main;
00595     d->multipartContents.clear();
00596   }
00597 }
00598 
00599 void Content::changeEncoding( Headers::contentEncoding e )
00600 {
00601   // This method makes no sense for encapsulated messages, they are always 7bit
00602   // encoded.
00603   Q_ASSERT( !bodyIsMessage() );
00604 
00605   Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00606   if( enc->encoding() == e ) {
00607     // Nothing to do.
00608     return;
00609   }
00610 
00611   if( decodeText() ) {
00612     // This is textual content.  Textual content is stored decoded.
00613     Q_ASSERT( enc->decoded() );
00614     enc->setEncoding( e );
00615   } else {
00616     // This is non-textual content.  Re-encode it.
00617     if( e == Headers::CEbase64 ) {
00618       d_ptr->body = KCodecs::base64Encode( decodedContent(), true );
00619       d_ptr->body.append( "\n" );
00620       enc->setEncoding( e );
00621       enc->setDecoded( false );
00622     } else {
00623       // It only makes sense to convert binary stuff to base64.
00624       Q_ASSERT( false );
00625     }
00626   }
00627 }
00628 
00629 void Content::toStream( QTextStream &ts, bool scrambleFromLines )
00630 {
00631   QByteArray ret = encodedContent( false );
00632 
00633   if ( scrambleFromLines ) {
00634     // FIXME Why are only From lines with a preceding empty line considered?
00635     //       And, of course, all lines starting with >*From have to be escaped
00636     //       because otherwise the transformation is not revertable.
00637     ret.replace( "\n\nFrom ", "\n\n>From ");
00638   }
00639   ts << ret;
00640 }
00641 
00642 Headers::Generic *Content::getNextHeader( QByteArray &head )
00643 {
00644   return d_ptr->nextHeader( head );
00645 }
00646 
00647 Headers::Generic *Content::nextHeader( QByteArray &head )
00648 {
00649   return d_ptr->nextHeader( head );
00650 }
00651 
00652 Headers::Generic *ContentPrivate::nextHeader( QByteArray &_head )
00653 {
00654   Headers::Base *header = HeaderParsing::extractFirstHeader( _head );
00655   if ( !header ) {
00656     return 0;
00657   }
00658   // Convert it from the real class to Generic.
00659   Headers::Generic *ret = new Headers::Generic( header->type(), q_ptr );
00660   ret->from7BitString( header->as7BitString() );
00661   return ret;
00662 }
00663 
00664 Headers::Base *Content::getHeaderByType( const char *type )
00665 {
00666   return headerByType( type );
00667 }
00668 
00669 Headers::Base *Content::headerByType( const char *type )
00670 {
00671   Q_ASSERT( type  && *type );
00672 
00673   foreach( Headers::Base *h, h_eaders ) {
00674     if( h->is( type ) ) {
00675       return h; // Found.
00676     }
00677   }
00678 
00679   return 0; // Not found.
00680 }
00681 
00682 Headers::Base::List Content::headersByType( const char *type )
00683 {
00684   Q_ASSERT( type && *type );
00685 
00686   Headers::Base::List result;
00687 
00688   foreach( Headers::Base *h, h_eaders ) {
00689     if( h->is( type ) ) {
00690       result << h;
00691     }
00692   }
00693 
00694   return result;
00695 }
00696 
00697 void Content::setHeader( Headers::Base *h )
00698 {
00699   Q_ASSERT( h );
00700   removeHeader( h->type() );
00701   appendHeader( h );
00702 }
00703 
00704 void Content::appendHeader( Headers::Base *h )
00705 {
00706   h_eaders.append( h );
00707   h->setParent( this );
00708 }
00709 
00710 void Content::prependHeader( Headers::Base *h )
00711 {
00712   h_eaders.prepend( h );
00713   h->setParent( this );
00714 }
00715 
00716 bool Content::removeHeader( const char *type )
00717 {
00718   for ( Headers::Base::List::iterator it = h_eaders.begin();
00719         it != h_eaders.end(); ++it )
00720     if ( (*it)->is(type) ) {
00721       delete (*it);
00722       h_eaders.erase( it );
00723       return true;
00724     }
00725 
00726   return false;
00727 }
00728 
00729 bool Content::hasHeader( const char *type )
00730 {
00731   return headerByType( type ) != 0;
00732 }
00733 
00734 int Content::size()
00735 {
00736   int ret = d_ptr->body.length();
00737 
00738   if ( contentTransferEncoding()->encoding() == Headers::CEbase64 ) {
00739     return ret * 3 / 4; //base64 => 6 bit per byte
00740   }
00741 
00742   // Not handling quoted-printable here since that requires actually
00743   // converting the content, and that is O(size_of_content).
00744   // For quoted-printable, this is only an approximate size.
00745 
00746   return ret;
00747 }
00748 
00749 int Content::storageSize() const
00750 {
00751   const Q_D(Content);
00752   int s = d->head.size();
00753 
00754   if ( d->contents().isEmpty() ) {
00755     s += d->body.size();
00756   } else {
00757 
00758     // FIXME: This should take into account the boundary headers that are added in
00759     //        encodedContent!
00760     foreach ( Content *c, d->contents() ) {
00761       s += c->storageSize();
00762     }
00763   }
00764 
00765   return s;
00766 }
00767 
00768 int Content::lineCount() const
00769 {
00770   const Q_D(Content);
00771   int ret = 0;
00772   if ( !isTopLevel() ) {
00773     ret += d->head.count( '\n' );
00774   }
00775   ret += d->body.count( '\n' );
00776 
00777   foreach ( Content *c, d->contents() ) {
00778     ret += c->lineCount();
00779   }
00780 
00781   return ret;
00782 }
00783 
00784 QByteArray Content::rawHeader( const char *name ) const
00785 {
00786   return KMime::extractHeader( d_ptr->head, name );
00787 }
00788 
00789 QList<QByteArray> Content::rawHeaders( const char *name ) const
00790 {
00791   return KMime::extractHeaders( d_ptr->head, name );
00792 }
00793 
00794 bool Content::decodeText()
00795 {
00796   Q_D(Content);
00797   Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00798 
00799   if ( !contentType()->isText() ) {
00800     return false; //non textual data cannot be decoded here => use decodedContent() instead
00801   }
00802   if ( enc->decoded() ) {
00803     return true; //nothing to do
00804   }
00805 
00806   switch( enc->encoding() )
00807   {
00808   case Headers::CEbase64 :
00809     d->body = KCodecs::base64Decode( d->body );
00810     d->body.append( "\n" );
00811     break;
00812   case Headers::CEquPr :
00813     d->body = KCodecs::quotedPrintableDecode( d->body );
00814     break;
00815   case Headers::CEuuenc :
00816     d->body = KCodecs::uudecode( d->body );
00817     d->body.append( "\n" );
00818     break;
00819   case Headers::CEbinary :
00820     // nothing to decode
00821     d->body.append( "\n" );
00822   default :
00823     break;
00824   }
00825 
00826   enc->setDecoded( true );
00827   return true;
00828 }
00829 
00830 QByteArray Content::defaultCharset() const
00831 {
00832   return d_ptr->defaultCS;
00833 }
00834 
00835 void Content::setDefaultCharset( const QByteArray &cs )
00836 {
00837   d_ptr->defaultCS = KMime::cachedCharset( cs );
00838 
00839   foreach ( Content *c, d_ptr->contents() ) {
00840     c->setDefaultCharset( cs );
00841   }
00842 
00843   // reparse the part and its sub-parts in order
00844   // to clear cached header values
00845   parse();
00846 }
00847 
00848 bool Content::forceDefaultCharset() const
00849 {
00850   return d_ptr->forceDefaultCS;
00851 }
00852 
00853 void Content::setForceDefaultCharset( bool b )
00854 {
00855   d_ptr->forceDefaultCS = b;
00856 
00857   foreach ( Content *c, d_ptr->contents() ) {
00858     c->setForceDefaultCharset( b );
00859   }
00860 
00861   // reparse the part and its sub-parts in order
00862   // to clear cached header values
00863   parse();
00864 }
00865 
00866 Content * KMime::Content::content( const ContentIndex &index ) const
00867 {
00868   if ( !index.isValid() ) {
00869     return const_cast<KMime::Content*>( this );
00870   }
00871   ContentIndex idx = index;
00872   unsigned int i = idx.pop() - 1; // one-based -> zero-based index
00873   if ( i < (unsigned int)d_ptr->contents().size() ) {
00874     return d_ptr->contents()[i]->content( idx );
00875   } else {
00876     return 0;
00877   }
00878 }
00879 
00880 ContentIndex KMime::Content::indexForContent( Content * content ) const
00881 {
00882   int i = d_ptr->contents().indexOf( content );
00883   if ( i >= 0 ) {
00884     ContentIndex ci;
00885     ci.push( i + 1 ); // zero-based -> one-based index
00886     return ci;
00887   }
00888   // not found, we need to search recursively
00889   for ( int i = 0; i < d_ptr->contents().size(); ++i ) {
00890     ContentIndex ci = d_ptr->contents()[i]->indexForContent( content );
00891     if ( ci.isValid() ) {
00892       // found it
00893       ci.push( i + 1 ); // zero-based -> one-based index
00894       return ci;
00895     }
00896   }
00897   return ContentIndex(); // not found
00898 }
00899 
00900 bool Content::isTopLevel() const
00901 {
00902   return d_ptr->parent == 0;
00903 }
00904 
00905 void Content::setParent( Content* parent )
00906 {
00907   //make sure the Content is only in the contents list of one parent object
00908   Content *oldParent = d_ptr->parent;
00909   if ( oldParent && oldParent->contents().contains( this ) ) {
00910     oldParent->removeContent( this );
00911   }
00912 
00913   d_ptr->parent = parent;
00914   if ( parent && !parent->contents().contains( this ) ) {
00915     parent->addContent( this );
00916   }
00917 }
00918 
00919 Content* Content::parent() const
00920 {
00921   return d_ptr->parent;
00922 }
00923 
00924 Content* Content::topLevel() const
00925 {
00926   Content *top = const_cast<Content*>(this);
00927   Content *c = parent();
00928   while ( c ) {
00929     top = c;
00930     c = c->parent();
00931   }
00932 
00933   return top;
00934 }
00935 
00936 ContentIndex Content::index() const
00937 {
00938   Content* top = topLevel();
00939   if ( top ) {
00940     return top->indexForContent( const_cast<Content*>(this) );
00941   }
00942 
00943   return indexForContent( const_cast<Content*>(this)  );
00944 }
00945 
00946 Message::Ptr Content::bodyAsMessage() const
00947 {
00948   if ( bodyIsMessage() && d_ptr->bodyAsMessage ) {
00949     return d_ptr->bodyAsMessage;
00950   } else {
00951     return Message::Ptr();
00952   }
00953 }
00954 
00955 bool Content::bodyIsMessage() const
00956 {
00957   // Use const_case here to work around API issue that neither header() nor hasHeader() are
00958   // const, even though they should be
00959   return const_cast<Content*>( this )->header<Headers::ContentType>( false ) &&
00960          const_cast<Content*>( this )->header<Headers::ContentType>( true )
00961                  ->mimeType().toLower() == "message/rfc822";
00962 }
00963 
00964 // @cond PRIVATE
00965 #define kmime_mk_header_accessor( type, method ) \
00966 Headers::type *Content::method( bool create ) { \
00967   return header<Headers::type>( create ); \
00968 }
00969 
00970 kmime_mk_header_accessor( ContentType, contentType )
00971 kmime_mk_header_accessor( ContentTransferEncoding, contentTransferEncoding )
00972 kmime_mk_header_accessor( ContentDisposition, contentDisposition )
00973 kmime_mk_header_accessor( ContentDescription, contentDescription )
00974 kmime_mk_header_accessor( ContentLocation, contentLocation )
00975 kmime_mk_header_accessor( ContentID, contentID )
00976 
00977 #undef kmime_mk_header_accessor
00978 // @endcond
00979 
00980 
00981 void ContentPrivate::clearBodyMessage()
00982 {
00983   bodyAsMessage.reset();
00984 }
00985 
00986 Content::List ContentPrivate::contents() const
00987 {
00988   Q_ASSERT( multipartContents.isEmpty() || !bodyAsMessage );
00989   if ( bodyAsMessage )
00990     return Content::List() << bodyAsMessage.get();
00991   else
00992     return multipartContents;
00993 }
00994 
00995 bool ContentPrivate::parseUuencoded()
00996 {
00997   Q_Q( Content );
00998   Parser::UUEncoded uup( body, KMime::extractHeader( head, "Subject" ) );
00999   if( !uup.parse() ) {
01000     return false; // Parsing failed.
01001   }
01002 
01003   Headers::ContentType *ct = q->contentType();
01004   ct->clear();
01005 
01006   if( uup.isPartial() ) {
01007     // This seems to be only a part of the message, so we treat it as "message/partial".
01008     ct->setMimeType( "message/partial" );
01009     //ct->setId( uniqueString() ); not needed yet
01010     ct->setPartialParams( uup.partialCount(), uup.partialNumber() );
01011     q->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01012   } else {
01013     // This is a complete message, so treat it as "multipart/mixed".
01014     body.clear();
01015     ct->setMimeType( "multipart/mixed" );
01016     ct->setBoundary( multiPartBoundary() );
01017     ct->setCategory( Headers::CCcontainer );
01018     q->contentTransferEncoding()->clear(); // 7Bit, decoded.
01019 
01020     // Add the plain text part first.
01021     Q_ASSERT( multipartContents.count() == 0 );
01022     {
01023       Content *c = new Content( q );
01024       c->contentType()->setMimeType( "text/plain" );
01025       c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01026       c->setBody( uup.textPart() );
01027       multipartContents.append( c );
01028     }
01029 
01030     // Now add each of the binary parts as sub-Contents.
01031     for( int i = 0; i < uup.binaryParts().count(); ++i ) {
01032       Content *c = new Content( q );
01033       c->contentType()->setMimeType( uup.mimeTypes().at( i ) );
01034       c->contentType()->setName( QLatin1String( uup.filenames().at( i ) ), QByteArray( /*charset*/ ) );
01035       c->contentTransferEncoding()->setEncoding( Headers::CEuuenc );
01036       c->contentTransferEncoding()->setDecoded( false );
01037       c->contentDisposition()->setDisposition( Headers::CDattachment );
01038       c->contentDisposition()->setFilename( QLatin1String( uup.filenames().at( i ) ) );
01039       c->setBody( uup.binaryParts().at( i ) );
01040       c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01041       multipartContents.append( c );
01042     }
01043   }
01044 
01045   return true; // Parsing successful.
01046 }
01047 
01048 bool ContentPrivate::parseYenc()
01049 {
01050   Q_Q( Content );
01051   Parser::YENCEncoded yenc( body );
01052   if( !yenc.parse() ) {
01053     return false; // Parsing failed.
01054   }
01055 
01056   Headers::ContentType *ct = q->contentType();
01057   ct->clear();
01058 
01059   if( yenc.isPartial() ) {
01060     // Assume there is exactly one decoded part.  Treat this as "message/partial".
01061     ct->setMimeType( "message/partial" );
01062     //ct->setId( uniqueString() ); not needed yet
01063     ct->setPartialParams( yenc.partialCount(), yenc.partialNumber() );
01064     q->contentTransferEncoding()->setEncoding( Headers::CEbinary );
01065     q->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01066   } else {
01067     // This is a complete message, so treat it as "multipart/mixed".
01068     body.clear();
01069     ct->setMimeType( "multipart/mixed" );
01070     ct->setBoundary( multiPartBoundary() );
01071     ct->setCategory( Headers::CCcontainer );
01072     q->contentTransferEncoding()->clear(); // 7Bit, decoded.
01073 
01074     // Add the plain text part first.
01075     Q_ASSERT( multipartContents.count() == 0 );
01076     {
01077       Content *c = new Content( q );
01078       c->contentType()->setMimeType( "text/plain" );
01079       c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
01080       c->setBody( yenc.textPart() );
01081       multipartContents.append( c );
01082     }
01083 
01084     // Now add each of the binary parts as sub-Contents.
01085     for ( int i=0; i<yenc.binaryParts().count(); i++ ) {
01086       Content *c = new Content( q );
01087       c->contentType()->setMimeType( yenc.mimeTypes().at( i ) );
01088       c->contentType()->setName( QLatin1String( yenc.filenames().at( i ) ), QByteArray( /*charset*/ ) );
01089       c->contentTransferEncoding()->setEncoding( Headers::CEbinary );
01090       c->contentDisposition()->setDisposition( Headers::CDattachment );
01091       c->contentDisposition()->setFilename( QLatin1String( yenc.filenames().at( i ) ) );
01092       c->setBody( yenc.binaryParts().at( i ) ); // Yenc bodies are binary.
01093       c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
01094       multipartContents.append( c );
01095     }
01096   }
01097 
01098   return true; // Parsing successful.
01099 }
01100 
01101 bool ContentPrivate::parseMultipart()
01102 {
01103   Q_Q( Content );
01104   const Headers::ContentType *ct = q->contentType();
01105   const QByteArray boundary = ct->boundary();
01106   if( boundary.isEmpty() ) {
01107     return false; // Parsing failed; invalid multipart content.
01108   }
01109   Parser::MultiPart mpp( body, boundary );
01110   if( !mpp.parse() ) {
01111     return false; // Parsing failed.
01112   }
01113 
01114   preamble = mpp.preamble();
01115   epilogue = mpp.epilouge();
01116 
01117   // Determine the category of the subparts (used in attachments()).
01118   Headers::contentCategory cat;
01119   if( ct->isSubtype( "alternative" ) ) {
01120     cat = Headers::CCalternativePart;
01121   } else {
01122     cat = Headers::CCmixedPart; // Default to "mixed".
01123   }
01124 
01125   // Create a sub-Content for every part.
01126   Q_ASSERT( multipartContents.isEmpty() );
01127   body.clear();
01128   QList<QByteArray> parts = mpp.parts();
01129   foreach( const QByteArray &part, mpp.parts() ) {
01130     Content *c = new Content( q );
01131     c->setContent( part );
01132     c->setFrozen( frozen );
01133     c->parse();
01134     c->contentType()->setCategory( cat );
01135     multipartContents.append( c );
01136   }
01137 
01138   return true; // Parsing successful.
01139 }
01140 
01141 } // namespace KMime

KMIME Library

Skip menu "KMIME Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • 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.5
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