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