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