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