00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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( Content *parent )
00061 : d_ptr( new ContentPrivate( this ) )
00062 {
00063 d_ptr->parent = parent;
00064 }
00065
00066 Content::Content( const QByteArray &h, const QByteArray &b )
00067 : d_ptr( new ContentPrivate( this ) )
00068 {
00069 d_ptr->head = h;
00070 d_ptr->body = b;
00071 }
00072
00073 Content::Content( const QByteArray &h, const QByteArray &b, Content *parent )
00074 : d_ptr( new ContentPrivate( this ) )
00075 {
00076 d_ptr->head = h;
00077 d_ptr->body = b;
00078 d_ptr->parent = parent;
00079 }
00080
00081 Content::Content( ContentPrivate *d ) :
00082 d_ptr( d )
00083 {
00084 }
00085
00086 Content::~Content()
00087 {
00088 qDeleteAll( h_eaders );
00089 h_eaders.clear();
00090 delete d_ptr;
00091 }
00092
00093 bool Content::hasContent() const
00094 {
00095 return !d_ptr->head.isEmpty() || !d_ptr->body.isEmpty() || !d_ptr->contents.isEmpty();
00096 }
00097
00098 void Content::setContent( const QList<QByteArray> &l )
00099 {
00100 Q_D(Content);
00101
00102 d->head.clear();
00103 d->body.clear();
00104
00105
00106 QTextStream hts( &( d->head ), QIODevice::WriteOnly );
00107 QTextStream bts( &( d->body ), QIODevice::WriteOnly );
00108 hts.setCodec( "ISO 8859-1" );
00109 bts.setCodec( "ISO 8859-1" );
00110
00111 bool isHead = true;
00112 foreach ( const QByteArray& line, l ) {
00113 if ( isHead && line.isEmpty() ) {
00114 isHead = false;
00115 continue;
00116 }
00117 if ( isHead ) {
00118 hts << line << "\n";
00119 } else {
00120 bts << line << "\n";
00121 }
00122 }
00123
00124
00125 }
00126
00127 void Content::setContent( const QByteArray &s )
00128 {
00129 Q_D(Content);
00130 d->head.clear();
00131 d->body.clear();
00132
00133
00134 if ( s.startsWith( '\n' ) ) {
00135 d->body = s.right( s.length() - 1 );
00136 return;
00137 }
00138
00139 int pos = s.indexOf( "\n\n", 0 );
00140 if ( pos > -1 ) {
00141 d->head = s.left( ++pos );
00142 d->body = s.mid( pos + 1, s.length() - pos - 1 );
00143 } else {
00144 d->head = s;
00145 }
00146 }
00147
00148 QByteArray Content::head() const
00149 {
00150 return d_ptr->head;
00151 }
00152
00153 void Content::setHead( const QByteArray &head )
00154 {
00155 d_ptr->head = head;
00156 }
00157
00158 QByteArray Content::body() const
00159 {
00160 return d_ptr->body;
00161 }
00162
00163 void Content::setBody( const QByteArray &body )
00164 {
00165 d_ptr->body = body;
00166 }
00167
00168
00169 void Content::parse()
00170 {
00171 Q_D(Content);
00172
00173 qDeleteAll( h_eaders );
00174 h_eaders.clear();
00175
00176
00177
00178
00179 if ( d->body.size() == 0 && !d->contents.isEmpty() ) {
00180
00181 foreach ( Content *c, d->contents ) {
00182 c->parse();
00183 }
00184 return;
00185 }
00186
00187 qDeleteAll( d->contents );
00188 d->contents.clear();
00189
00190 Headers::ContentType *ct = contentType();
00191 QByteArray tmp;
00192 Content *c;
00193 Headers::contentCategory cat;
00194
00195
00196
00197
00198
00199 if ( ct->mimeType() == "text" || ct->isEmpty() ) {
00200
00201 Parser::UUEncoded uup( d->body, rawHeader( "Subject" ) );
00202
00203 if ( uup.parse() ) {
00204
00205 if ( uup.isPartial() ) {
00206
00207
00208 ct->setMimeType( "message/partial" );
00209
00210 ct->setPartialParams( uup.partialCount(), uup.partialNumber() );
00211 contentTransferEncoding()->setEncoding( Headers::CE7Bit );
00212 } else {
00213
00214
00215 d->body.clear();
00216
00217
00218 for ( int i = 0; i < uup.binaryParts().count(); ++i ) {
00219 c = new Content( this );
00220
00221 tmp = "Content-Type: ";
00222 tmp += uup.mimeTypes().at( i );
00223 tmp += "; name=\"";
00224 tmp += uup.filenames().at( i );
00225 tmp += "\"\nContent-Transfer-Encoding: x-uuencode\nContent-Disposition: attachment; filename=\"";
00226 tmp += uup.filenames().at( i );
00227 tmp += "\"\n\n";
00228 tmp += uup.binaryParts().at( i );
00229 c->setContent( tmp );
00230 addContent( c );
00231 }
00232
00233 if ( !d->contents.isEmpty() && d->contents.first() ) {
00234
00235 d->contents.first()->setContent(
00236 "Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n" +
00237 uup.textPart() );
00238 d->contents.first()->contentType()->setMimeType( "text/plain" );
00239 }
00240 }
00241 } else {
00242 Parser::YENCEncoded yenc( d->body );
00243
00244 if ( yenc.parse() ) {
00245
00246
00247 if ( yenc.isPartial() ) {
00248 ct->setMimeType( "message/partial" );
00249
00250 ct->setPartialParams( yenc.partialCount(), yenc.partialNumber() );
00251 contentTransferEncoding()->setEncoding( Headers::CEbinary );
00252 } else {
00253
00254
00255 d->body.clear();
00256
00257
00258 for ( int i=0; i<yenc.binaryParts().count(); i++ ) {
00259 c = new Content( this );
00260
00261 tmp = "Content-Type: ";
00262 tmp += yenc.mimeTypes().at( i );
00263 tmp += "; name=\"";
00264 tmp += yenc.filenames().at( i );
00265 tmp += "\"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment; filename=\"";
00266 tmp += yenc.filenames().at( i );
00267 tmp += "\"\n\n";
00268 c->setContent( tmp );
00269
00270
00271 c->setBody( yenc.binaryParts()[i] );
00272
00273 addContent( c );
00274 }
00275
00276 if ( !d->contents.isEmpty() && d->contents.first() ) {
00277
00278 d->contents.first()->setContent(
00279 "Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n" +
00280 yenc.textPart() );
00281 d->contents.first()->contentType()->setMimeType( "text/plain" );
00282 }
00283 }
00284 }
00285 }
00286 }
00287
00288
00289 if ( ct->isText() ) {
00290 return;
00291 }
00292
00293
00294 if ( ct->isMultipart() ) {
00295 tmp = ct->boundary();
00296
00297 if ( !tmp.isEmpty() ) {
00298 Parser::MultiPart mpp( d->body, tmp );
00299 if ( mpp.parse() ) {
00300
00301 if ( ct->isSubtype( "alternative" ) ) {
00302 cat = Headers::CCalternativePart;
00303 } else {
00304 cat = Headers::CCmixedPart;
00305 }
00306
00307 QList<QByteArray> parts = mpp.parts();
00308 QList<QByteArray>::Iterator it;
00309 for ( it=parts.begin(); it != parts.end(); ++it ) {
00310
00311 c = new Content( this );
00312 c->setContent( *it );
00313 c->parse();
00314 c->contentType()->setCategory( cat );
00315 d->contents.append( c );
00316
00317 }
00318
00319
00320 d->body.clear();
00321 } else {
00322 ct->setMimeType( "text/plain" );
00323 ct->setCharset( "US-ASCII" );
00324 }
00325 }
00326 }
00327 }
00328
00329 void Content::assemble()
00330 {
00331 Q_D(Content);
00332 QByteArray newHead = assembleHeaders();
00333 foreach ( Headers::Base *h, h_eaders ) {
00334 if ( h->isXHeader() ) {
00335 newHead += h->as7BitString() + '\n';
00336 KMime::removeHeader( d->head, h->type() );
00337 }
00338 }
00339 newHead += d->head;
00340 d->head = newHead;
00341
00342 foreach ( Content *c, contents() ) {
00343 c->assemble();
00344 }
00345 }
00346
00347 QByteArray Content::assembleHeaders()
00348 {
00349 Q_D(Content);
00350 QByteArray newHead;
00351
00352
00353 Headers::Base *h = contentType( false );
00354 if ( h && !h->isEmpty() ) {
00355 newHead += contentType()->as7BitString() + '\n';
00356 KMime::removeHeader( d->head, h->type() );
00357 }
00358
00359
00360 h = contentTransferEncoding( false );
00361 if ( h && !h->isEmpty() ) {
00362 newHead += contentTransferEncoding()->as7BitString() + '\n';
00363 KMime::removeHeader( d->head, h->type() );
00364 }
00365
00366
00367 h = contentDescription( false );
00368 if ( h ) {
00369 newHead += h->as7BitString() + '\n';
00370 KMime::removeHeader( d->head, h->type() );
00371 }
00372
00373
00374 h = contentDisposition( false );
00375 if ( h ) {
00376 newHead += h->as7BitString() + '\n';
00377 KMime::removeHeader( d->head, h->type() );
00378 }
00379
00380 return newHead;
00381 }
00382
00383 void Content::clear()
00384 {
00385 Q_D(Content);
00386 qDeleteAll( h_eaders );
00387 h_eaders.clear();
00388 qDeleteAll( d->contents );
00389 d->contents.clear();
00390 d->head.clear();
00391 d->body.clear();
00392 }
00393
00394 QByteArray Content::encodedContent( bool useCrLf )
00395 {
00396 Q_D(Content);
00397 QByteArray e;
00398
00399
00400
00401 if ( !d->contents.isEmpty() ) {
00402 bool convertNonMimeBinaries=false;
00403
00404
00405 foreach ( Content *c, d->contents ) {
00406 if ( ( c->contentTransferEncoding( true )->encoding() == Headers::CEuuenc ) ||
00407 ( c->contentTransferEncoding( true )->encoding() == Headers::CEbinary ) ) {
00408 convertNonMimeBinaries = true;
00409 c->setBody( KCodecs::base64Encode( c->decodedContent(), true ) + '\n' );
00410 c->contentTransferEncoding( true )->setEncoding(Headers::CEbase64);
00411 c->contentTransferEncoding( true )->setDecoded( false );
00412 c->removeHeader("Content-Description");
00413 c->assemble();
00414 }
00415 }
00416
00417
00418 if ( convertNonMimeBinaries ) {
00419 int beg = 0, end = 0;
00420 beg = d->head.indexOf( "MIME-Version: " );
00421 if ( beg >= 0 ) {
00422 end = d->head.indexOf( '\n', beg );
00423 }
00424 if ( beg >= 0 && end > beg ) {
00425 d->head.remove( beg, end - beg );
00426 }
00427 beg = d->head.indexOf( "Content-Type: " );
00428 if ( beg >= 0 ) {
00429 end = d->head.indexOf( '\n', beg );
00430 }
00431 if ( beg >= 0 && end > beg ) {
00432 d->head.remove( beg, end - beg );
00433 }
00434 beg = d->head.indexOf( "Content-Transfer-Encoding: " );
00435 if ( beg >= 0 ) {
00436 end = d->head.indexOf( '\n', beg );
00437 }
00438 if ( beg >= 0 && end > beg ) {
00439 d->head.remove( beg, end - beg );
00440 }
00441
00442 d->head += "MIME-Version: 1.0\n";
00443 d->head += contentType( true )->as7BitString() + '\n';
00444 d->head += contentTransferEncoding( true )->as7BitString() + '\n';
00445 }
00446 }
00447
00448
00449 e = d->head;
00450 e += '\n';
00451
00452
00453 if ( !d->body.isEmpty() ) {
00454 Headers::ContentTransferEncoding *enc=contentTransferEncoding();
00455
00456 if (enc->needToEncode()) {
00457 if ( enc->encoding() == Headers::CEquPr ) {
00458 e += KCodecs::quotedPrintableEncode( d->body, false );
00459 } else {
00460 e += KCodecs::base64Encode( d->body, true );
00461 e += '\n';
00462 }
00463 } else {
00464 e += d->body;
00465 }
00466 }
00467 else if ( !d->contents.isEmpty() ) {
00468 Headers::ContentType *ct=contentType();
00469 QByteArray boundary = "\n--" + ct->boundary();
00470
00471
00472 foreach ( Content *c, d->contents ) {
00473 e+=boundary + '\n';
00474 e += c->encodedContent( false );
00475 }
00476
00477 e += boundary+"--\n";
00478 };
00479
00480 if ( useCrLf ) {
00481 return LFtoCRLF( e );
00482 } else {
00483 return e;
00484 }
00485 }
00486
00487 QByteArray Content::decodedContent()
00488 {
00489 QByteArray temp, ret;
00490 Headers::ContentTransferEncoding *ec=contentTransferEncoding();
00491 bool removeTrailingNewline=false;
00492 int size = d_ptr->body.length();
00493
00494 if ( size == 0 ) {
00495 return ret;
00496 }
00497
00498 temp.resize( size );
00499 memcpy( temp.data(), d_ptr->body.data(), size );
00500
00501 if ( ec->decoded() ) {
00502 ret = temp;
00503 removeTrailingNewline = true;
00504 } else {
00505 switch( ec->encoding() ) {
00506 case Headers::CEbase64 :
00507 KCodecs::base64Decode( temp, ret );
00508 break;
00509 case Headers::CEquPr :
00510 ret = KCodecs::quotedPrintableDecode( d_ptr->body );
00511 ret.resize( ret.size() - 1 );
00512 removeTrailingNewline = true;
00513 break;
00514 case Headers::CEuuenc :
00515 KCodecs::uudecode( temp, ret );
00516 break;
00517 case Headers::CEbinary :
00518 ret = temp;
00519 removeTrailingNewline = false;
00520 break;
00521 default :
00522 ret = temp;
00523 removeTrailingNewline = true;
00524 }
00525 }
00526
00527 if ( removeTrailingNewline && ( ret.size() > 0 ) && ( ret[ret.size()-1] == '\n') ) {
00528 ret.resize( ret.size() - 1 );
00529 }
00530
00531 return ret;
00532 }
00533
00534 QString Content::decodedText( bool trimText, bool removeTrailingNewlines )
00535 {
00536 if ( !decodeText() ) {
00537 return QString();
00538 }
00539
00540 bool ok = true;
00541 QTextCodec *codec =
00542 KGlobal::charsets()->codecForName( contentType()->charset(), ok );
00543
00544 QString s = codec->toUnicode( d_ptr->body.data(), d_ptr->body.length() );
00545
00546 if ( trimText && removeTrailingNewlines ) {
00547 int i;
00548 for ( i = s.length() - 1; i >= 0; --i ) {
00549 if ( !s[i].isSpace() ) {
00550 break;
00551 }
00552 }
00553 s.truncate( i + 1 );
00554 } else {
00555 if ( s.right( 1 ) == "\n" ) {
00556 s.truncate( s.length() - 1 );
00557 }
00558 }
00559
00560 return s;
00561 }
00562
00563 void Content::fromUnicodeString( const QString &s )
00564 {
00565 bool ok = true;
00566 QTextCodec *codec =
00567 KGlobal::charsets()->codecForName( contentType()->charset(), ok );
00568
00569 if ( !ok ) {
00570 codec = KGlobal::locale()->codecForEncoding();
00571 QByteArray chset = KGlobal::locale()->encoding();
00572 contentType()->setCharset( chset );
00573 }
00574
00575 d_ptr->body = codec->fromUnicode( s );
00576 contentTransferEncoding()->setDecoded( true );
00577 }
00578
00579 Content *Content::textContent()
00580 {
00581 Content *ret=0;
00582
00583
00584 if ( contentType()->isText() ) {
00585 ret = this;
00586 } else {
00587 foreach ( Content *c, d_ptr->contents ) {
00588 if ( ( ret = c->textContent() ) != 0 ) {
00589 break;
00590 }
00591 }
00592 }
00593 return ret;
00594 }
00595
00596 Content::List Content::attachments( bool incAlternatives )
00597 {
00598 List attachments;
00599 if ( d_ptr->contents.isEmpty() ) {
00600 attachments.append( this );
00601 } else {
00602 foreach ( Content *c, d_ptr->contents ) {
00603 if ( !incAlternatives &&
00604 c->contentType()->category() == Headers::CCalternativePart ) {
00605 continue;
00606 } else {
00607 attachments += c->attachments( incAlternatives );
00608 }
00609 }
00610 }
00611
00612 if ( isTopLevel() ) {
00613 Content *text = textContent();
00614 if ( text ) {
00615 attachments.removeAll( text );
00616 }
00617 }
00618 return attachments;
00619 }
00620
00621 Content::List Content::contents() const
00622 {
00623 return d_ptr->contents;
00624 }
00625
00626 void Content::addContent( Content *c, bool prepend )
00627 {
00628 Q_D(Content);
00629 if ( d->contents.isEmpty() && !contentType()->isMultipart() ) {
00630
00631
00632
00633 Content *main = new Content( this );
00634
00635
00636 for ( Headers::Base::List::iterator it = h_eaders.begin();
00637 it != h_eaders.end(); ) {
00638 if ( (*it)->isMimeHeader() ) {
00639
00640 main->h_eaders.append( *it );
00641
00642 it = h_eaders.erase( it );
00643 } else {
00644 ++it;
00645 }
00646 }
00647
00648
00649 main->contentType()->setCategory(Headers::CCmixedPart);
00650
00651
00652 main->assemble();
00653
00654
00655 main->setBody( d->body );
00656 d->contents.append( main );
00657 d->body.clear();
00658
00659
00660 Headers::ContentType *ct=contentType();
00661 ct->setMimeType( "multipart/mixed" );
00662 ct->setBoundary( multiPartBoundary() );
00663 ct->setCategory( Headers::CCcontainer );
00664 contentTransferEncoding()->clear();
00665
00666 }
00667
00668
00669 if ( prepend ) {
00670 d->contents.insert( 0, c );
00671 } else {
00672 d->contents.append( c );
00673 }
00674
00675 if ( c->parent() != this )
00676 c->setParent(this);
00677 }
00678
00679 void Content::removeContent( Content *c, bool del )
00680 {
00681 Q_D(Content);
00682 if ( d->contents.isEmpty() ) {
00683 return;
00684 }
00685
00686 d->contents.removeAll( c );
00687 if ( del ) {
00688 delete c;
00689 } else {
00690 c->setParent( 0 );
00691 }
00692
00693
00694 if ( d->contents.count() == 1 ) {
00695 Content *main = d->contents.first();
00696
00697
00698 for ( Headers::Base::List::iterator it = main->h_eaders.begin();
00699 it != main->h_eaders.end(); ) {
00700 if ( (*it)->isMimeHeader() ) {
00701 kDebug(5320) << "Content::removeContent(Content *c, bool del) : mime-header moved:"
00702 << (*it)->as7BitString();
00703
00704 removeHeader( (*it)->type() );
00705
00706 h_eaders.append( *it );
00707
00708 it = main->h_eaders.erase( it );
00709 } else {
00710 ++it;
00711 }
00712 }
00713
00714
00715 d->body = main->body();
00716
00717
00718 qDeleteAll( d->contents );
00719 d->contents.clear();
00720 }
00721 }
00722
00723 void Content::changeEncoding( Headers::contentEncoding e )
00724 {
00725 Headers::ContentTransferEncoding *enc = contentTransferEncoding();
00726 if ( enc->encoding() == e ) {
00727 return;
00728 }
00729
00730 if ( decodeText() ) {
00731 enc->setEncoding( e );
00732
00733 } else {
00734
00735 if ( e != Headers::CEbase64 ) {
00736
00737
00738
00739 e = Headers::CEbase64;
00740 }
00741
00742 if ( enc->encoding() != e ) {
00743 d_ptr->body = KCodecs::base64Encode( decodedContent(), true );
00744 d_ptr->body.append( "\n" );
00745 enc->setEncoding( e );
00746 enc->setDecoded( false );
00747 }
00748 }
00749 }
00750
00751 void Content::toStream( QTextStream &ts, bool scrambleFromLines )
00752 {
00753 QByteArray ret = encodedContent( false );
00754
00755 if ( scrambleFromLines ) {
00756
00757
00758
00759 ret.replace( "\n\nFrom ", "\n\n>From ");
00760 }
00761 ts << ret;
00762 }
00763
00764 Headers::Generic *Content::getNextHeader( QByteArray &head )
00765 {
00766 return nextHeader( head );
00767 }
00768
00769 Headers::Generic *Content::nextHeader( QByteArray &head )
00770 {
00771 int pos1=-1, pos2=0, len=head.length()-1;
00772 bool folded( false );
00773 Headers::Generic *header=0;
00774
00775 pos1 = head.indexOf( ": " );
00776
00777 if ( pos1 > -1 ) {
00778 pos2 = pos1 += 2;
00779
00780 if ( head[pos2] != '\n' ) {
00781 while ( 1 ) {
00782 pos2 = head.indexOf( '\n', pos2 + 1 );
00783 if ( pos2 == -1 || pos2 == len ||
00784 ( head[pos2+1] != ' ' && head[pos2+1] != '\t' ) ) {
00785
00786 break;
00787 } else {
00788 folded = true;
00789 }
00790 }
00791 }
00792
00793 if ( pos2 < 0 ) {
00794 pos2 = len + 1;
00795 }
00796
00797 if ( !folded ) {
00798 header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1));
00799 } else {
00800 QByteArray hdrValue = head.mid( pos1, pos2 - pos1 );
00801 header = new Headers::Generic( head.left( pos1 - 2 ), this, unfoldHeader( hdrValue ) );
00802 }
00803
00804 head.remove( 0, pos2 + 1 );
00805 } else {
00806 head = "";
00807 }
00808
00809 return header;
00810 }
00811
00812 Headers::Base *Content::getHeaderByType( const char *type )
00813 {
00814 return headerByType( type );
00815 }
00816
00817 Headers::Base *Content::headerByType( const char *type )
00818 {
00819 if ( !type ) {
00820 return 0;
00821 }
00822
00823
00824 foreach ( Headers::Base *h, h_eaders ) {
00825 if ( h->is( type ) ) {
00826 return h;
00827 }
00828 }
00829
00830
00831 Headers::Base *h = 0;
00832 QByteArray raw=rawHeader( type );
00833 if ( !raw.isEmpty() ) {
00834
00835 if ( strcasecmp( "Message-Id", type ) == 0 ) {
00836 h = new Headers::MessageID( this, raw );
00837 } else if ( strcasecmp( "Subject", type ) == 0 ) {
00838 h = new Headers::Subject( this, raw );
00839 } else if ( strcasecmp( "Date", type ) == 0 ) {
00840 h = new Headers::Date( this, raw );
00841 } else if ( strcasecmp( "From", type ) == 0 ) {
00842 h = new Headers::From( this, raw );
00843 } else if ( strcasecmp( "Organization", type ) == 0 ) {
00844 h = new Headers::Organization( this, raw );
00845 } else if ( strcasecmp( "Reply-To", type ) == 0 ) {
00846 h = new Headers::ReplyTo( this, raw );
00847 } else if ( strcasecmp( "Mail-Copies-To", type ) == 0 ) {
00848 h = new Headers::MailCopiesTo( this, raw );
00849 } else if ( strcasecmp( "To", type ) == 0 ) {
00850 h = new Headers::To( this, raw );
00851 } else if ( strcasecmp( "CC", type ) == 0 ) {
00852 h = new Headers::Cc( this, raw );
00853 } else if ( strcasecmp( "BCC", type ) == 0 ) {
00854 h = new Headers::Bcc( this, raw );
00855 } else if ( strcasecmp( "Newsgroups", type ) == 0 ) {
00856 h = new Headers::Newsgroups( this, raw );
00857 } else if ( strcasecmp( "Followup-To", type ) == 0 ) {
00858 h = new Headers::FollowUpTo( this, raw );
00859 } else if ( strcasecmp( "References", type ) == 0 ) {
00860 h = new Headers::References( this, raw );
00861 } else if ( strcasecmp( "Lines", type ) == 0 ) {
00862 h = new Headers::Lines( this, raw );
00863 } else if ( strcasecmp( "Content-Type", type ) == 0 ) {
00864 h = new Headers::ContentType( this, raw );
00865 } else if ( strcasecmp( "Content-Transfer-Encoding", type ) == 0 ) {
00866 h = new Headers::ContentTransferEncoding( this, raw );
00867 } else if ( strcasecmp( "Content-Disposition", type ) == 0 ) {
00868 h = new Headers::ContentDisposition( this, raw );
00869 } else if ( strcasecmp( "Content-Description", type ) == 0 ) {
00870 h = new Headers::ContentDescription( this, raw );
00871 } else if ( strcasecmp( "Content-Location", type ) == 0 ) {
00872 h = new Headers::ContentLocation( this, raw );
00873 } else if ( strcasecmp( "Sender", type ) == 0 ) {
00874 h = new Headers::Sender( this, raw );
00875 } else {
00876 h = new Headers::Generic( type, this, raw );
00877 }
00878 h_eaders.append( h );
00879 return h;
00880 } else {
00881 return 0;
00882 }
00883 }
00884
00885 QList<Headers::Base*> Content::headersByType( const char *type )
00886 {
00887 QList<Headers::Base*> result;
00888
00889 if ( !type ) {
00890 return result;
00891 }
00892
00893 QList<QByteArray> raw=rawHeaders( type );
00894 foreach( QByteArray header, raw )
00895 result.append( new Headers::Generic( type, this, header ) );
00896 return result;
00897 }
00898
00899 void Content::setHeader( Headers::Base *h )
00900 {
00901 if ( !h ) {
00902 return;
00903 }
00904 removeHeader( h->type() );
00905 h_eaders.append( h );
00906 }
00907
00908 bool Content::removeHeader( const char *type )
00909 {
00910 for ( Headers::Base::List::iterator it = h_eaders.begin();
00911 it != h_eaders.end(); ++it )
00912 if ( (*it)->is(type) ) {
00913 delete (*it);
00914 h_eaders.erase( it );
00915 return true;
00916 }
00917
00918 return false;
00919 }
00920
00921 bool Content::hasHeader( const char *type )
00922 {
00923 return headerByType( type ) != 0;
00924 }
00925
00926 Headers::ContentType *Content::contentType( bool create )
00927 {
00928 Headers::ContentType *p=0;
00929 return headerInstance( p, create );
00930 }
00931
00932 Headers::ContentTransferEncoding *Content::contentTransferEncoding( bool create )
00933 {
00934 Headers::ContentTransferEncoding *p=0;
00935 return headerInstance( p, create );
00936 }
00937
00938 Headers::ContentDisposition *Content::contentDisposition( bool create )
00939 {
00940 Headers::ContentDisposition *p=0;
00941 return headerInstance( p, create );
00942 }
00943
00944 Headers::ContentDescription *Content::contentDescription( bool create )
00945 {
00946 Headers::ContentDescription *p=0;
00947 return headerInstance( p, create );
00948 }
00949
00950 Headers::ContentLocation *Content::contentLocation( bool create )
00951 {
00952 Headers::ContentLocation *p=0;
00953 return headerInstance( p, create );
00954 }
00955
00956 int Content::size()
00957 {
00958 int ret = d_ptr->body.length();
00959
00960 if ( contentTransferEncoding()->encoding() == Headers::CEbase64 ) {
00961 return ret * 3 / 4;
00962 }
00963
00964 return ret;
00965 }
00966
00967 int Content::storageSize() const
00968 {
00969 const Q_D(Content);
00970 int s = d->head.size();
00971
00972 if ( d->contents.isEmpty() ) {
00973 s += d->body.size();
00974 } else {
00975 foreach ( Content *c, d->contents ) {
00976 s += c->storageSize();
00977 }
00978 }
00979
00980 return s;
00981 }
00982
00983 int Content::lineCount() const
00984 {
00985 const Q_D(Content);
00986 int ret = 0;
00987 if ( !isTopLevel() ) {
00988 ret += d->head.count( '\n' );
00989 }
00990 ret += d->body.count( '\n' );
00991
00992 foreach ( Content *c, d->contents ) {
00993 ret += c->lineCount();
00994 }
00995
00996 return ret;
00997 }
00998
00999 QByteArray Content::rawHeader( const char *name ) const
01000 {
01001 return KMime::extractHeader( d_ptr->head, name );
01002 }
01003
01004 QList<QByteArray> Content::rawHeaders( const char *name ) const
01005 {
01006 return KMime::extractHeaders( d_ptr->head, name );
01007 }
01008
01009 bool Content::decodeText()
01010 {
01011 Q_D(Content);
01012 Headers::ContentTransferEncoding *enc = contentTransferEncoding();
01013
01014 if ( !contentType()->isText() ) {
01015 return false;
01016 }
01017 if ( enc->decoded() ) {
01018 return true;
01019 }
01020
01021 switch( enc->encoding() )
01022 {
01023 case Headers::CEbase64 :
01024 d->body = KCodecs::base64Decode( d->body );
01025 d->body.append( "\n" );
01026 break;
01027 case Headers::CEquPr :
01028 d->body = KCodecs::quotedPrintableDecode( d->body );
01029 break;
01030 case Headers::CEuuenc :
01031 d->body = KCodecs::uudecode( d->body );
01032 d->body.append( "\n" );
01033 break;
01034 case Headers::CEbinary :
01035
01036 d->body.append( "\n" );
01037 default :
01038 break;
01039 }
01040
01041 enc->setDecoded( true );
01042 return true;
01043 }
01044
01045 QByteArray Content::defaultCharset() const
01046 {
01047 return d_ptr->defaultCS;
01048 }
01049
01050 void Content::setDefaultCharset( const QByteArray &cs )
01051 {
01052 d_ptr->defaultCS = KMime::cachedCharset( cs );
01053
01054 foreach ( Content *c, d_ptr->contents ) {
01055 c->setDefaultCharset( cs );
01056 }
01057
01058
01059
01060 parse();
01061 }
01062
01063 bool Content::forceDefaultCharset() const
01064 {
01065 return d_ptr->forceDefaultCS;
01066 }
01067
01068 void Content::setForceDefaultCharset( bool b )
01069 {
01070 d_ptr->forceDefaultCS = b;
01071
01072 foreach ( Content *c, d_ptr->contents ) {
01073 c->setForceDefaultCharset( b );
01074 }
01075
01076
01077
01078 parse();
01079 }
01080
01081 Content * KMime::Content::content( const ContentIndex &index ) const
01082 {
01083 if ( !index.isValid() ) {
01084 return const_cast<KMime::Content*>( this );
01085 }
01086 ContentIndex idx = index;
01087 unsigned int i = idx.pop() - 1;
01088 if ( i < (unsigned int)d_ptr->contents.size() ) {
01089 return d_ptr->contents[i]->content( idx );
01090 } else {
01091 return 0;
01092 }
01093 }
01094
01095 ContentIndex KMime::Content::indexForContent( Content * content ) const
01096 {
01097 int i = d_ptr->contents.indexOf( content );
01098 if ( i >= 0 ) {
01099 ContentIndex ci;
01100 ci.push( i + 1 );
01101 return ci;
01102 }
01103
01104 for ( int i = 0; i < d_ptr->contents.size(); ++i ) {
01105 ContentIndex ci = d_ptr->contents[i]->indexForContent( content );
01106 if ( ci.isValid() ) {
01107
01108 ci.push( i + 1 );
01109 return ci;
01110 }
01111 }
01112 return ContentIndex();
01113 }
01114
01115 bool Content::isTopLevel() const
01116 {
01117 return false;
01118 }
01119
01120 void Content::setParent( Content* parent )
01121 {
01122
01123 Content *oldParent = d_ptr->parent;
01124 if ( oldParent && oldParent->contents().contains( this ) ) {
01125 oldParent->removeContent( this );
01126 }
01127
01128 d_ptr->parent = parent;
01129 if ( parent && !parent->contents().contains( this ) ) {
01130 parent->addContent( this );
01131 }
01132 }
01133
01134 Content* Content::parent() const
01135 {
01136 return d_ptr->parent;
01137 }
01138
01139 Content* Content::topLevel() const
01140 {
01141 Content *top = const_cast<Content*>(this);
01142 Content *c = parent();
01143 while ( c ) {
01144 top = c;
01145 c = c->parent();
01146 }
01147
01148 return top;
01149 }
01150
01151 ContentIndex Content::index() const
01152 {
01153 Content* top = topLevel();
01154 if ( top ) {
01155 return top->indexForContent( const_cast<Content*>(this) );
01156 }
01157
01158 return indexForContent( const_cast<Content*>(this) );
01159 }
01160
01161
01162
01163 }