diff --git a/kmime/kmime_content.cpp b/kmime/kmime_content.cpp index 480995584..3731ed384 100644 --- a/kmime/kmime_content.cpp +++ b/kmime/kmime_content.cpp @@ -1,1069 +1,1049 @@ /* kmime_content.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details Copyright (c) 2006 Volker Krause Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling @ref MIME data and defines the Content class. @brief Defines the Content class. @authors the KMime authors (see AUTHORS file), Volker Krause \ */ #include "kmime_content.h" #include "kmime_content_p.h" #include "kmime_header_parsing.h" #include "kmime_parsers.h" #include "kmime_util_p.h" #include #include #include #include #include #include #include #include using namespace KMime; namespace KMime { Content::Content() : d_ptr( new ContentPrivate( this ) ) { } Content::Content( Content *parent ) : d_ptr( new ContentPrivate( this ) ) { d_ptr->parent = parent; } Content::Content( const QByteArray &h, const QByteArray &b ) : d_ptr( new ContentPrivate( this ) ) { d_ptr->head = h; d_ptr->body = b; } Content::Content( const QByteArray &h, const QByteArray &b, Content *parent ) : d_ptr( new ContentPrivate( this ) ) { d_ptr->head = h; d_ptr->body = b; d_ptr->parent = parent; } Content::Content( ContentPrivate *d ) : d_ptr( d ) { } Content::~Content() { qDeleteAll( h_eaders ); h_eaders.clear(); delete d_ptr; d_ptr = 0; } bool Content::hasContent() const { return !d_ptr->head.isEmpty() || !d_ptr->body.isEmpty() || !d_ptr->contents.isEmpty(); } void Content::setContent( const QList &l ) { Q_D(Content); //qDebug("Content::setContent( const QList &l ) : start"); d->head.clear(); d->body.clear(); //usage of textstreams is much faster than simply appending the strings QTextStream hts( &( d->head ), QIODevice::WriteOnly ); QTextStream bts( &( d->body ), QIODevice::WriteOnly ); hts.setCodec( "ISO 8859-1" ); bts.setCodec( "ISO 8859-1" ); bool isHead = true; foreach ( const QByteArray& line, l ) { if ( isHead && line.isEmpty() ) { isHead = false; continue; } if ( isHead ) { hts << line << "\n"; } else { bts << line << "\n"; } } //qDebug("Content::setContent( const QList & l ) : finished"); } void Content::setContent( const QByteArray &s ) { Q_D(Content); d->head.clear(); d->body.clear(); // empty header if ( s.startsWith( '\n' ) ) { d->body = s.right( s.length() - 1 ); return; } int pos = s.indexOf( "\n\n", 0 ); if ( pos > -1 ) { d->head = s.left( ++pos ); //header *must* end with "\n" !! d->body = s.mid( pos + 1, s.length() - pos - 1 ); } else { d->head = s; } } QByteArray Content::head() const { return d_ptr->head; } void Content::setHead( const QByteArray &head ) { d_ptr->head = head; if ( !head.endsWith( '\n' ) ) d_ptr->head += '\n'; } QByteArray Content::body() const { return d_ptr->body; } void Content::setBody( const QByteArray &body ) { d_ptr->body = body; } void Content::parse() { Q_D( Content ); // Clean up old headers and parse them again. qDeleteAll( h_eaders ); h_eaders = HeaderParsing::parseHeaders( d->head ); foreach( Headers::Base *h, h_eaders ) { h->setParent( this ); } // If we are frozen, save the body as-is. This is done because parsing // changes the content (it loses preambles and epilogues, converts uuencode->mime, etc.) if( d->frozen ) { d->frozenBody = d->body; } // Clean up old sub-Contents and parse them again. qDeleteAll( d->contents ); d->contents.clear(); Headers::ContentType *ct = contentType(); if( ct->isText() ) { // This content is either text, or of unknown type. - kDebug() << "Content-Type empty or text."; if( d->parseUuencoded() ) { // This is actually uuencoded content generated by broken software. - kDebug() << "Parsed uuencoded content."; } else if( d->parseYenc() ) { // This is actually yenc content generated by broken software. - kDebug() << "Parsed yenc content."; } else { // This is just plain text. - kDebug() << "Parsed plain text."; } } else if( ct->isMultipart() ) { // This content claims to be MIME multipart. - kDebug() << "Content-Type is multipart."; if( d->parseMultipart() ) { // This is actual MIME multipart content. - kDebug() << "Parsed MIME multipart."; } else { // Parsing failed; treat this content as "text/plain". kWarning() << "Failed to parse multipart. Treating as text/plain."; ct->setMimeType( "text/plain" ); ct->setCharset( "US-ASCII" ); } } else { // This content is something else, like an image or something... - kDebug() << "Content-Type is" << ct->mimeType() << "; leaving untouched."; } } bool Content::isFrozen() const { return d_ptr->frozen; } void Content::setFrozen( bool frozen ) { d_ptr->frozen = frozen; } void Content::assemble() { Q_D( Content ); if( d->frozen ) { - kDebug() << "Frozen; doing nothing."; return; } d->head = assembleHeaders(); foreach( Content *c, contents() ) { c->assemble(); } } QByteArray Content::assembleHeaders() { QByteArray newHead; foreach( const Headers::Base *h, h_eaders ) { - kDebug() << "header" << h->type() << h->as7BitString(); if( !h->isEmpty() ) { newHead += h->as7BitString() + '\n'; } } return newHead; } void Content::clear() { Q_D(Content); qDeleteAll( h_eaders ); h_eaders.clear(); clearContents(); d->head.clear(); d->body.clear(); } void Content::clearContents( bool del ) { Q_D(Content); if( del ) { qDeleteAll( d->contents ); } d->contents.clear(); } QByteArray Content::encodedContent( bool useCrLf ) { Q_D(Content); QByteArray e; // Head. e = d->head; e += '\n'; // Body. if( d->frozen ) { // This Content is frozen. if( d->frozenBody.isEmpty() ) { // This Content has never been parsed. e += d->body; } else { // Use the body as it was before parsing. e += d->frozenBody; } } else if( !d->body.isEmpty() ) { // This is a single-part Content. Headers::ContentTransferEncoding *enc = contentTransferEncoding(); Q_ASSERT( enc->encoding() != Headers::CEuuenc ); Q_ASSERT( enc->encoding() != Headers::CEbinary ); if (enc->needToEncode()) { if ( enc->encoding() == Headers::CEquPr ) { e += KCodecs::quotedPrintableEncode( d->body, false ); } else { e += KCodecs::base64Encode( d->body, true ); e += '\n'; } } else { e += d->body; } } else if ( !d->contents.isEmpty() ) { // This is a multipart Content. Headers::ContentType *ct=contentType(); QByteArray boundary = "\n--" + ct->boundary(); //add all (encoded) contents separated by boundaries foreach ( Content *c, d->contents ) { e+=boundary + '\n'; e += c->encodedContent( false ); // don't convert LFs here, we do that later!!!!! } //finally append the closing boundary e += boundary+"--\n"; }; if ( useCrLf ) { return LFtoCRLF( e ); } else { return e; } } QByteArray Content::decodedContent() { QByteArray temp, ret; Headers::ContentTransferEncoding *ec=contentTransferEncoding(); bool removeTrailingNewline=false; int size = d_ptr->body.length(); if ( size == 0 ) { return ret; } temp.resize( size ); memcpy( temp.data(), d_ptr->body.data(), size ); if ( ec->decoded() ) { ret = temp; removeTrailingNewline = true; } else { switch( ec->encoding() ) { case Headers::CEbase64 : KCodecs::base64Decode( temp, ret ); break; case Headers::CEquPr : ret = KCodecs::quotedPrintableDecode( d_ptr->body ); ret.resize( ret.size() - 1 ); // remove null-char removeTrailingNewline = true; break; case Headers::CEuuenc : KCodecs::uudecode( temp, ret ); break; case Headers::CEbinary : ret = temp; removeTrailingNewline = false; break; default : ret = temp; removeTrailingNewline = true; } } if ( removeTrailingNewline && ( ret.size() > 0 ) && ( ret[ret.size()-1] == '\n') ) { ret.resize( ret.size() - 1 ); } return ret; } QString Content::decodedText( bool trimText, bool removeTrailingNewlines ) { if ( !decodeText() ) { //this is not a text content !! return QString(); } bool ok = true; QTextCodec *codec = KGlobal::charsets()->codecForName( contentType()->charset(), ok ); QString s = codec->toUnicode( d_ptr->body.data(), d_ptr->body.length() ); if ( trimText && removeTrailingNewlines ) { int i; for ( i = s.length() - 1; i >= 0; --i ) { if ( !s[i].isSpace() ) { break; } } s.truncate( i + 1 ); } else { if ( s.right( 1 ) == "\n" ) { s.truncate( s.length() - 1 ); // remove trailing new-line } } return s; } void Content::fromUnicodeString( const QString &s ) { bool ok = true; QTextCodec *codec = KGlobal::charsets()->codecForName( contentType()->charset(), ok ); if ( !ok ) { // no suitable codec found => try local settings and hope the best ;-) codec = KGlobal::locale()->codecForEncoding(); QByteArray chset = KGlobal::locale()->encoding(); contentType()->setCharset( chset ); } d_ptr->body = codec->fromUnicode( s ); contentTransferEncoding()->setDecoded( true ); //text is always decoded } Content *Content::textContent() { Content *ret=0; //return the first content with mimetype=text/* if ( contentType()->isText() ) { ret = this; } else { foreach ( Content *c, d_ptr->contents ) { if ( ( ret = c->textContent() ) != 0 ) { break; } } } return ret; } Content::List Content::attachments( bool incAlternatives ) { List attachments; if ( d_ptr->contents.isEmpty() ) { attachments.append( this ); } else { foreach ( Content *c, d_ptr->contents ) { if ( !incAlternatives && c->contentType()->category() == Headers::CCalternativePart ) { continue; } else { attachments += c->attachments( incAlternatives ); } } } if ( isTopLevel() ) { Content *text = textContent(); if ( text ) { attachments.removeAll( text ); } } return attachments; } Content::List Content::contents() const { return d_ptr->contents; } void Content::addContent( Content *c, bool prepend ) { Q_D( Content ); // If this message is single-part; make it multipart first. if( d->contents.isEmpty() && !contentType()->isMultipart() ) { // The current body will be our first sub-Content. Content *main = new Content( this ); // Move the MIME headers to the newly created sub-Content. // NOTE: The other headers (RFC5322 headers like From:, To:, as well as X-headers // are not moved to the subcontent; they remain with the top-level content. for ( Headers::Base::List::iterator it = h_eaders.begin(); it != h_eaders.end(); ) { if ( (*it)->isMimeHeader() ) { - kDebug() << "moving header" << (*it)->type() << (*it)->as7BitString(); // Add to new content. main->setHeader( *it ); // Remove from this content. it = h_eaders.erase( it ); } else { - kDebug() << "skipping header" << (*it)->type() << (*it)->as7BitString(); ++it; } } // Adjust the Content-Type of the newly created sub-Content. main->contentType()->setCategory( Headers::CCmixedPart ); // Move the body to the new subcontent. main->setBody( d->body ); d->body.clear(); - { - main->assemble(); // For debugging only. - kDebug() << "head of new subcontent" << main->head(); - } - // Add the subcontent. d->contents.append( main ); // Convert this content to "multipart/mixed". Headers::ContentType *ct = contentType(); ct->setMimeType( "multipart/mixed" ); ct->setBoundary( multiPartBoundary() ); ct->setCategory( Headers::CCcontainer ); contentTransferEncoding()->clear(); // 7Bit, decoded. } // Add the new content. if( prepend ) { d->contents.prepend( c ); } else { d->contents.append( c ); } if( c->parent() != this ) { // If the content was part of something else, this will remove it from there. c->setParent( this ); } } void Content::removeContent( Content *c, bool del ) { Q_D( Content ); Q_ASSERT( d->contents.contains( c ) ); d->contents.removeAll( c ); if ( del ) { delete c; } else { c->d_ptr->parent = 0; } // If only one content is left, turn this content into a single-part. if( d->contents.count() == 1 ) { Content *main = d->contents.first(); // Move all headers from the old subcontent to ourselves. // NOTE: This also sets the new Content-Type. foreach( Headers::Base *h, main->h_eaders ) { - kDebug() << "moving header" << h->type() << h->as7BitString(); setHeader( h ); // Will remove the old one if present. } main->h_eaders.clear(); // Move the body. d->body = main->body(); // Delete the old subcontent. delete main; d->contents.clear(); } } void Content::changeEncoding( Headers::contentEncoding e ) { Headers::ContentTransferEncoding *enc = contentTransferEncoding(); if( enc->encoding() == e ) { // Nothing to do. return; } if( decodeText() ) { // This is textual content. Textual content is stored decoded. Q_ASSERT( enc->decoded() ); enc->setEncoding( e ); } else { // This is non-textual content. Re-encode it. if( e == Headers::CEbase64 ) { d_ptr->body = KCodecs::base64Encode( decodedContent(), true ); d_ptr->body.append( "\n" ); enc->setEncoding( e ); enc->setDecoded( false ); } else { // It only makes sense to convert binary stuff to base64. Q_ASSERT( false ); } } } void Content::toStream( QTextStream &ts, bool scrambleFromLines ) { QByteArray ret = encodedContent( false ); if ( scrambleFromLines ) { // FIXME Why are only From lines with a preceding empty line considered? // And, of course, all lines starting with >*From have to be escaped // because otherwise the transformation is not revertable. ret.replace( "\n\nFrom ", "\n\n>From "); } ts << ret; } Headers::Generic *Content::getNextHeader( QByteArray &head ) { return nextHeader( head ); } Headers::Generic *Content::nextHeader( QByteArray &head ) { Headers::Base *header = HeaderParsing::extractFirstHeader( head ); // Convert it from the real class to Generic. Headers::Generic *ret = new Headers::Generic( header->type(), this ); ret->from7BitString( header->as7BitString() ); return ret; } Headers::Base *Content::getHeaderByType( const char *type ) { return headerByType( type ); } Headers::Base *Content::headerByType( const char *type ) { - kDebug() << "Type" << type; Q_ASSERT( type && *type ); foreach( Headers::Base *h, h_eaders ) { if( h->is( type ) ) { - kDebug() << "Found" << h->as7BitString(); return h; // Found. } } - kDebug() << "Not found."; return 0; // Not found. } Headers::Base::List Content::headersByType( const char *type ) { Q_ASSERT( type && *type ); Headers::Base::List result; foreach( Headers::Base *h, h_eaders ) { if( h->is( type ) ) { result << h; } } return result; } void Content::setHeader( Headers::Base *h ) { Q_ASSERT( h ); removeHeader( h->type() ); appendHeader( h ); } void Content::appendHeader( Headers::Base *h ) { h_eaders.append( h ); h->setParent( this ); } void Content::prependHeader( Headers::Base *h ) { h_eaders.prepend( h ); h->setParent( this ); } bool Content::removeHeader( const char *type ) { for ( Headers::Base::List::iterator it = h_eaders.begin(); it != h_eaders.end(); ++it ) if ( (*it)->is(type) ) { delete (*it); h_eaders.erase( it ); return true; } return false; } bool Content::hasHeader( const char *type ) { return headerByType( type ) != 0; } int Content::size() { int ret = d_ptr->body.length(); if ( contentTransferEncoding()->encoding() == Headers::CEbase64 ) { return ret * 3 / 4; //base64 => 6 bit per byte } // Not handling quoted-printable here since that requires actually // converting the content, and that is O(size_of_content). // For quoted-printable, this is only an approximate size. return ret; } int Content::storageSize() const { const Q_D(Content); int s = d->head.size(); if ( d->contents.isEmpty() ) { s += d->body.size(); } else { foreach ( Content *c, d->contents ) { s += c->storageSize(); } } return s; } int Content::lineCount() const { const Q_D(Content); int ret = 0; if ( !isTopLevel() ) { ret += d->head.count( '\n' ); } ret += d->body.count( '\n' ); foreach ( Content *c, d->contents ) { ret += c->lineCount(); } return ret; } QByteArray Content::rawHeader( const char *name ) const { return KMime::extractHeader( d_ptr->head, name ); } QList Content::rawHeaders( const char *name ) const { return KMime::extractHeaders( d_ptr->head, name ); } bool Content::decodeText() { Q_D(Content); Headers::ContentTransferEncoding *enc = contentTransferEncoding(); if ( !contentType()->isText() ) { return false; //non textual data cannot be decoded here => use decodedContent() instead } if ( enc->decoded() ) { return true; //nothing to do } switch( enc->encoding() ) { case Headers::CEbase64 : d->body = KCodecs::base64Decode( d->body ); d->body.append( "\n" ); break; case Headers::CEquPr : d->body = KCodecs::quotedPrintableDecode( d->body ); break; case Headers::CEuuenc : d->body = KCodecs::uudecode( d->body ); d->body.append( "\n" ); break; case Headers::CEbinary : // nothing to decode d->body.append( "\n" ); default : break; } enc->setDecoded( true ); return true; } QByteArray Content::defaultCharset() const { return d_ptr->defaultCS; } void Content::setDefaultCharset( const QByteArray &cs ) { d_ptr->defaultCS = KMime::cachedCharset( cs ); foreach ( Content *c, d_ptr->contents ) { c->setDefaultCharset( cs ); } // reparse the part and its sub-parts in order // to clear cached header values parse(); } bool Content::forceDefaultCharset() const { return d_ptr->forceDefaultCS; } void Content::setForceDefaultCharset( bool b ) { d_ptr->forceDefaultCS = b; foreach ( Content *c, d_ptr->contents ) { c->setForceDefaultCharset( b ); } // reparse the part and its sub-parts in order // to clear cached header values parse(); } Content * KMime::Content::content( const ContentIndex &index ) const { if ( !index.isValid() ) { return const_cast( this ); } ContentIndex idx = index; unsigned int i = idx.pop() - 1; // one-based -> zero-based index if ( i < (unsigned int)d_ptr->contents.size() ) { return d_ptr->contents[i]->content( idx ); } else { return 0; } } ContentIndex KMime::Content::indexForContent( Content * content ) const { int i = d_ptr->contents.indexOf( content ); if ( i >= 0 ) { ContentIndex ci; ci.push( i + 1 ); // zero-based -> one-based index return ci; } // not found, we need to search recursively for ( int i = 0; i < d_ptr->contents.size(); ++i ) { ContentIndex ci = d_ptr->contents[i]->indexForContent( content ); if ( ci.isValid() ) { // found it ci.push( i + 1 ); // zero-based -> one-based index return ci; } } return ContentIndex(); // not found } bool Content::isTopLevel() const { return false; } void Content::setParent( Content* parent ) { //make sure the Content is only in the contents list of one parent object Content *oldParent = d_ptr->parent; if ( oldParent && oldParent->contents().contains( this ) ) { oldParent->removeContent( this ); } d_ptr->parent = parent; if ( parent && !parent->contents().contains( this ) ) { parent->addContent( this ); } } Content* Content::parent() const { return d_ptr->parent; } Content* Content::topLevel() const { Content *top = const_cast(this); Content *c = parent(); while ( c ) { top = c; c = c->parent(); } return top; } ContentIndex Content::index() const { Content* top = topLevel(); if ( top ) { return top->indexForContent( const_cast(this) ); } return indexForContent( const_cast(this) ); } // @cond PRIVATE #define kmime_mk_header_accessor( type, method ) \ Headers::type *Content::method( bool create ) { \ return header( create ); \ } kmime_mk_header_accessor( ContentType, contentType ) kmime_mk_header_accessor( ContentTransferEncoding, contentTransferEncoding ) kmime_mk_header_accessor( ContentDisposition, contentDisposition ) kmime_mk_header_accessor( ContentDescription, contentDescription ) kmime_mk_header_accessor( ContentLocation, contentLocation ) kmime_mk_header_accessor( ContentID, contentID ) #undef kmime_mk_header_accessor // @endcond bool ContentPrivate::parseUuencoded() { Q_Q( Content ); Parser::UUEncoded uup( body, KMime::extractHeader( head, "Subject" ) ); if( !uup.parse() ) { return false; // Parsing failed. } Headers::ContentType *ct = q->contentType(); ct->clear(); if( uup.isPartial() ) { // This seems to be only a part of the message, so we treat it as "message/partial". ct->setMimeType( "message/partial" ); //ct->setId( uniqueString() ); not needed yet ct->setPartialParams( uup.partialCount(), uup.partialNumber() ); q->contentTransferEncoding()->setEncoding( Headers::CE7Bit ); } else { // This is a complete message, so treat it as "multipart/mixed". body.clear(); ct->setMimeType( "multipart/mixed" ); ct->setBoundary( multiPartBoundary() ); ct->setCategory( Headers::CCcontainer ); q->contentTransferEncoding()->clear(); // 7Bit, decoded. // Add the plain text part first. Q_ASSERT( contents.count() == 0 ); { Content *c = new Content( q ); c->contentType()->setMimeType( "text/plain" ); c->contentTransferEncoding()->setEncoding( Headers::CE7Bit ); c->setBody( uup.textPart() ); contents.append( c ); } // Now add each of the binary parts as sub-Contents. for( int i = 0; i < uup.binaryParts().count(); ++i ) { Content *c = new Content( q ); c->contentType()->setMimeType( uup.mimeTypes().at( i ) ); c->contentType()->setName( uup.filenames().at( i ), QByteArray( /*charset*/ ) ); c->contentTransferEncoding()->setEncoding( Headers::CEuuenc ); c->contentDisposition()->setDisposition( Headers::CDattachment ); c->contentDisposition()->setFilename( uup.filenames().at( i ) ); c->setBody( uup.binaryParts().at( i ) ); c->changeEncoding( Headers::CEbase64 ); // Convert to base64. contents.append( c ); } } return true; // Parsing successful. } bool ContentPrivate::parseYenc() { Q_Q( Content ); Parser::YENCEncoded yenc( body ); if( !yenc.parse() ) { return false; // Parsing failed. } Headers::ContentType *ct = q->contentType(); ct->clear(); if( yenc.isPartial() ) { // Assume there is exactly one decoded part. Treat this as "message/partial". ct->setMimeType( "message/partial" ); //ct->setId( uniqueString() ); not needed yet ct->setPartialParams( yenc.partialCount(), yenc.partialNumber() ); q->contentTransferEncoding()->setEncoding( Headers::CEbinary ); q->changeEncoding( Headers::CEbase64 ); // Convert to base64. } else { // This is a complete message, so treat it as "multipart/mixed". body.clear(); ct->setMimeType( "multipart/mixed" ); ct->setBoundary( multiPartBoundary() ); ct->setCategory( Headers::CCcontainer ); q->contentTransferEncoding()->clear(); // 7Bit, decoded. // Add the plain text part first. Q_ASSERT( contents.count() == 0 ); { Content *c = new Content( q ); c->contentType()->setMimeType( "text/plain" ); c->contentTransferEncoding()->setEncoding( Headers::CE7Bit ); c->setBody( yenc.textPart() ); contents.append( c ); } // Now add each of the binary parts as sub-Contents. for ( int i=0; icontentType()->setMimeType( yenc.mimeTypes().at( i ) ); c->contentType()->setName( yenc.filenames().at( i ), QByteArray( /*charset*/ ) ); c->contentTransferEncoding()->setEncoding( Headers::CEbinary ); c->contentDisposition()->setDisposition( Headers::CDattachment ); c->contentDisposition()->setFilename( yenc.filenames().at( i ) ); c->setBody( yenc.binaryParts().at( i ) ); // Yenc bodies are binary. c->changeEncoding( Headers::CEbase64 ); // Convert to base64. contents.append( c ); } } return true; // Parsing successful. } bool ContentPrivate::parseMultipart() { Q_Q( Content ); const Headers::ContentType *ct = q->contentType(); const QByteArray boundary = ct->boundary(); if( boundary.isEmpty() ) { return false; // Parsing failed; invalid multipart content. } Parser::MultiPart mpp( body, boundary ); if( !mpp.parse() ) { return false; // Parsing failed. } // Determine the category of the subparts (used in attachments()). Headers::contentCategory cat; if( ct->isSubtype( "alternative" ) ) { cat = Headers::CCalternativePart; } else { cat = Headers::CCmixedPart; // Default to "mixed". } // Create a sub-Content for every part. Q_ASSERT( contents.isEmpty() ); body.clear(); QList parts = mpp.parts(); foreach( const QByteArray &part, mpp.parts() ) { Content *c = new Content( q ); c->setContent( part ); c->setFrozen( frozen ); c->parse(); c->contentType()->setCategory( cat ); contents.append( c ); } return true; // Parsing successful. } } // namespace KMime diff --git a/kmime/kmime_headerfactory.cpp b/kmime/kmime_headerfactory.cpp index f7efb8613..86343f786 100644 --- a/kmime/kmime_headerfactory.cpp +++ b/kmime/kmime_headerfactory.cpp @@ -1,118 +1,117 @@ /* kmime_header_factory.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling MIME data and defines the HeaderFactory class. @brief Defines the HeaderFactory class. @authors Constantin Berzan \ */ #include "kmime_headerfactory.h" #include "kmime_headers.h" #include #include #include using namespace KMime; /** * @internal * Private class that helps to provide binary compatibility between releases. */ class KMime::HeaderFactoryPrivate { public: HeaderFactoryPrivate(); ~HeaderFactoryPrivate(); HeaderFactory *const instance; QHash headerMakers; // Type->obj mapping; with lower-case type. }; K_GLOBAL_STATIC( HeaderFactoryPrivate, sInstance ) HeaderFactoryPrivate::HeaderFactoryPrivate() : instance( new HeaderFactory( this ) ) { } HeaderFactoryPrivate::~HeaderFactoryPrivate() { qDeleteAll( headerMakers.values() ); delete instance; } HeaderFactory* HeaderFactory::self() { return sInstance->instance; } Headers::Base *HeaderFactory::createHeader( const QByteArray &type ) { Q_ASSERT( !type.isEmpty() ); const HeaderMakerBase *maker = d->headerMakers.value( type.toLower() ); if( maker ) { return maker->create(); } else { kError() << "Unknown header type" << type; //return new Headers::Generic; return 0; } } HeaderFactory::HeaderFactory( HeaderFactoryPrivate *dd ) : d( dd ) { } HeaderFactory::~HeaderFactory() { } bool HeaderFactory::registerHeaderMaker( const QByteArray &type, HeaderMakerBase *maker ) { if( type.isEmpty() ) { // This is probably a generic (but not abstract) header, // like Address or MailboxList. We cannot register those. kWarning() << "Tried to register header with empty type."; return false; } const QByteArray ltype = type.toLower(); if( d->headerMakers.contains( ltype ) ) { kWarning() << "Header of type" << type << "already registered."; // TODO should we make this an error? return false; } d->headerMakers.insert( ltype, maker ); - kDebug() << "Registered type" << type; return true; }