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