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

KDE's Doxygen guidelines are available online.

KMIME Library

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

kdepimlibs-4.8.5 API Reference

Skip menu "kdepimlibs-4.8.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal