diff --git a/kmime/kmime_headerfactory.cpp b/kmime/kmime_headerfactory.cpp index 054ef87e5..f7efb8613 100644 --- a/kmime/kmime_headerfactory.cpp +++ b/kmime/kmime_headerfactory.cpp @@ -1,118 +1,118 @@ /* 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 headers; // Type->obj mapping; with lower-case type. + QHash headerMakers; // Type->obj mapping; with lower-case type. }; K_GLOBAL_STATIC( HeaderFactoryPrivate, sInstance ) HeaderFactoryPrivate::HeaderFactoryPrivate() : instance( new HeaderFactory( this ) ) { } HeaderFactoryPrivate::~HeaderFactoryPrivate() { - qDeleteAll( headers.values() ); + qDeleteAll( headerMakers.values() ); delete instance; } HeaderFactory* HeaderFactory::self() { return sInstance->instance; } Headers::Base *HeaderFactory::createHeader( const QByteArray &type ) { Q_ASSERT( !type.isEmpty() ); - Headers::Base *h = d->headers.value( type.toLower() ); - if( h ) { - return h->clone(); + 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::registerHeader( Headers::Base *header ) +bool HeaderFactory::registerHeaderMaker( const QByteArray &type, HeaderMakerBase *maker ) { - if( QByteArray( header->type() ).isEmpty() ) { + 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; } - QByteArray ltype = QByteArray( header->type() ).toLower(); - if( d->headers.contains( ltype ) ) { - kWarning() << "Header of type" << header->type() << "already registered."; + 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->headers.insert( ltype, header ); - kDebug() << "registered type" << header->type(); + d->headerMakers.insert( ltype, maker ); + kDebug() << "Registered type" << type; return true; } diff --git a/kmime/kmime_headerfactory.h b/kmime/kmime_headerfactory.h index 315ba69a0..640a62f77 100644 --- a/kmime/kmime_headerfactory.h +++ b/kmime/kmime_headerfactory.h @@ -1,77 +1,95 @@ /* kmime_header_factory.h 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 @ref MIME data and defines the HeaderFactory class. @brief Defines the HeaderFactory class. @authors Constantin Berzan \ */ #ifndef __KMIME_HEADERFACTORY_H__ #define __KMIME_HEADERFACTORY_H__ #include "kmime_export.h" #include namespace KMime { namespace Headers { class Base; } +class HeaderMakerBase +{ + public: + virtual ~HeaderMakerBase() {} + virtual Headers::Base *create() const = 0; +}; + +template +class HeaderMaker : public HeaderMakerBase +{ + public: + virtual Headers::Base *create() const + { + return new T; + } +}; + class HeaderFactoryPrivate; /** docu TODO */ class KMIME_EXPORT HeaderFactory { public: static HeaderFactory* self(); template inline bool registerHeader() { - return registerHeader( new T ); + T dummy; + return registerHeaderMaker( QByteArray( dummy.type() ), new HeaderMaker() ); } Headers::Base *createHeader( const QByteArray &type ); private: HeaderFactory( HeaderFactoryPrivate *dd ); HeaderFactory( const HeaderFactory &other ); // undefined HeaderFactory& operator=( const HeaderFactory &other ); // undefined ~HeaderFactory(); - bool registerHeader( Headers::Base *header ); + bool registerHeaderMaker( const QByteArray &type, HeaderMakerBase *maker ); friend class HeaderFactoryPrivate; HeaderFactoryPrivate *const d; }; } // namespace KMime #endif /* __KMIME_HEADERFACTORY_H__ */ diff --git a/kmime/kmime_headers.cpp b/kmime/kmime_headers.cpp index d215cf854..18cf19e99 100644 --- a/kmime/kmime_headers.cpp +++ b/kmime/kmime_headers.cpp @@ -1,2174 +1,2152 @@ /* -*- c++ -*- kmime_headers.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 the KMime authors. See file AUTHORS for details Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public - License assert published by the Free Software Foundation; either + 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 morbe details. + 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 various header classes: - header's base class defining the common interface - generic base classes for different types of fields - incompatible, Structured-based field classes - compatible, Unstructured-based field classes @brief Defines the various headers classes. @authors the KMime authors (see AUTHORS file), Volker Krause \ */ #include "kmime_headers.h" #include "kmime_headers_p.h" #include "kmime_util.h" #include "kmime_content.h" #include "kmime_codecs.h" #include "kmime_header_parsing.h" #include "kmime_headerfactory.h" #include "kmime_warning.h" #include #include #include #include #include #include #include template bool registerHeaderHelper() { const T dummy; if( QByteArray( dummy.type() ).isEmpty() ) { // This is a generic header. return false; } return KMime::HeaderFactory::self()->registerHeader(); } // macro to register a header with HeaderFactory #define kmime_register_header( subclass ) \ namespace { const bool dummyForRegistering##subclass = registerHeaderHelper(); } // macro to generate a default constructor implementation #define kmime_mk_trivial_ctor( subclass, baseclass ) \ subclass::subclass( Content *parent ) : baseclass( parent ) \ { \ clear(); \ } \ \ subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( parent ) \ { \ from7BitString( s ); \ } \ \ subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \ baseclass( parent ) \ { \ fromUnicodeString( s, charset ); \ } \ \ -Base *subclass::clone() const \ -{ \ - subclass *ret = new subclass; \ - ret->from7BitString( as7BitString( false ) ); \ - return ret; \ -} \ - \ subclass::~subclass() {} \ \ kmime_register_header( subclass ) // end kmime_mk_trivial_ctor #define kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \ subclass::subclass( Content *parent ) : baseclass( new subclass##Private, parent ) \ { \ clear(); \ } \ \ subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( new subclass##Private, parent ) \ { \ from7BitString( s ); \ } \ \ subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \ baseclass( new subclass##Private, parent ) \ { \ fromUnicodeString( s, charset ); \ } \ \ -Base *subclass::clone() const \ -{ \ - subclass *ret = new subclass; \ - ret->from7BitString( as7BitString( false ) ); \ - return ret; \ -} \ - \ subclass::~subclass() {} \ \ kmime_register_header( subclass ) // end kmime_mk_trivial_ctor_with_dptr #define kmime_mk_trivial_ctor_with_name( subclass, baseclass, name ) \ kmime_mk_trivial_ctor( subclass, baseclass ) \ \ const char *subclass::type() const \ { \ return #name; \ } #define kmime_mk_trivial_ctor_with_name_and_dptr( subclass, baseclass, name ) \ kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \ const char *subclass::type() const { return #name; } #define kmime_mk_dptr_ctor( subclass, baseclass ) \ subclass::subclass( subclass##Private *d, KMime::Content *parent ) : baseclass( d, parent ) {} using namespace KMime; using namespace KMime::Headers; using namespace KMime::Types; using namespace KMime::HeaderParsing; namespace KMime { namespace Headers { //--------------------------------------- Base::Base( KMime::Content *parent ) : d_ptr( new BasePrivate ) { Q_D(Base); d->parent = parent; } Base::Base( BasePrivate *dd, KMime::Content *parent ) : d_ptr( dd ) { Q_D(Base); d->parent = parent; } Base::~Base() { delete d_ptr; d_ptr = 0; } KMime::Content *Base::parent() const { return d_ptr->parent; } void Base::setParent( KMime::Content *parent ) { d_ptr->parent = parent; } QByteArray Base::rfc2047Charset() const { if ( d_ptr->encCS.isEmpty() || forceDefaultCharset() ) { return defaultCharset(); } else { return d_ptr->encCS; } } void Base::setRFC2047Charset( const QByteArray &cs ) { d_ptr->encCS = cachedCharset( cs ); } bool Base::forceDefaultCharset() const { return ( parent() != 0 ? parent()->forceDefaultCharset() : false ); } QByteArray Base::defaultCharset() const { return ( parent() != 0 ? parent()->defaultCharset() : Latin1 ); } const char *Base::type() const { return ""; } bool Base::is( const char *t ) const { return strcasecmp( t, type() ) == 0; } bool Base::isMimeHeader() const { return strncasecmp( type(), "Content-", 8 ) == 0; } bool Base::isXHeader() const { return strncmp( type(), "X-", 2 ) == 0; } QByteArray Base::typeIntro() const { return QByteArray( type() ) + ": "; } //-------------------------------------- namespace Generics { //------------------------------ //@cond PRIVATE kmime_mk_dptr_ctor( Unstructured, Base ) //@endcond Unstructured::Unstructured( Content *p ) : Base( new UnstructuredPrivate, p ) { } Unstructured::Unstructured( Content *p, const QByteArray &s ) : Base( new UnstructuredPrivate, p ) { from7BitString( s ); } Unstructured::Unstructured( Content *p, const QString &s, const QByteArray &cs ) : Base( new UnstructuredPrivate, p ) { fromUnicodeString( s, cs ); } Unstructured::~Unstructured() { } void Unstructured::from7BitString( const QByteArray &s ) { Q_D(Unstructured); d->decoded = decodeRFC2047String( s, d->encCS, defaultCharset(), forceDefaultCharset() ); } QByteArray Unstructured::as7BitString( bool withHeaderType ) const { const Q_D(Unstructured); QByteArray result; if ( withHeaderType ) { result = typeIntro(); } result += encodeRFC2047String( d->decoded, d->encCS ) ; return result; } void Unstructured::fromUnicodeString( const QString &s, const QByteArray &b ) { Q_D(Unstructured); d->decoded = s; d->encCS = cachedCharset( b ); } QString Unstructured::asUnicodeString() const { return d_func()->decoded; } void Unstructured::clear() { Q_D(Unstructured); d->decoded.truncate( 0 ); } bool Unstructured::isEmpty() const { return d_func()->decoded.isEmpty(); } //------------------------------ //------------------------------ Structured::Structured( Content *p ) : Base( new StructuredPrivate, p ) { } Structured::Structured( Content *p, const QByteArray &s ) : Base( new StructuredPrivate, p ) { from7BitString( s ); } Structured::Structured( Content *p, const QString &s, const QByteArray &cs ) : Base( new StructuredPrivate, p ) { fromUnicodeString( s, cs ); } kmime_mk_dptr_ctor( Structured, Base ) Structured::~Structured() { } void Structured::from7BitString( const QByteArray &s ) { Q_D(Structured); if ( d->encCS.isEmpty() ) { d->encCS = defaultCharset(); } const char *cursor = s.constData(); parse( cursor, cursor + s.length() ); } QString Structured::asUnicodeString() const { return QString::fromLatin1( as7BitString( false ) ); } void Structured::fromUnicodeString( const QString &s, const QByteArray &b ) { Q_D(Structured); d->encCS = cachedCharset( b ); from7BitString( s.toLatin1() ); } //------------------------------ //-----
------------------------- Address::Address( Content *p ) : Structured( new AddressPrivate, p ) { } Address::Address( Content *p, const QByteArray &s ) : Structured( new AddressPrivate, p ) { from7BitString( s ); } Address::Address( Content *p, const QString &s, const QByteArray &cs ) : Structured( new AddressPrivate, p ) { fromUnicodeString( s, cs ); } kmime_mk_dptr_ctor( Address, Structured ) Address:: ~Address() { } // helper method used in AddressList and MailboxList static bool stringToMailbox( const QByteArray &address, const QString &displayName, Types::Mailbox &mbox ) { Types::AddrSpec addrSpec; mbox.setName( displayName ); const char *cursor = address.constData(); if ( !parseAngleAddr( cursor, cursor + address.length(), addrSpec ) ) { if ( !parseAddrSpec( cursor, cursor + address.length(), addrSpec ) ) { kWarning() << "Invalid address"; return false; } } mbox.setAddress( addrSpec ); return true; } //-----
------------------------- //------------------------------ kmime_mk_trivial_ctor_with_dptr( MailboxList, Address ) kmime_mk_dptr_ctor( MailboxList, Address ) QByteArray MailboxList::as7BitString( bool withHeaderType ) const { const Q_D(MailboxList); if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv = typeIntro(); } foreach ( Types::Mailbox mbox, d->mailboxList ) { rv += mbox.as7BitString( d->encCS ); rv += ", "; } rv.resize( rv.length() - 2 ); return rv; } void MailboxList::fromUnicodeString( const QString &s, const QByteArray &b ) { Q_D(MailboxList); d->encCS = cachedCharset( b ); from7BitString( encodeRFC2047String( s, b, false ) ); } QString MailboxList::asUnicodeString() const { return prettyAddresses().join( QLatin1String( ", " ) ); } void MailboxList::clear() { Q_D(MailboxList); d->mailboxList.clear(); } bool MailboxList::isEmpty() const { return d_func()->mailboxList.isEmpty(); } void MailboxList::addAddress( const Types::Mailbox &mbox ) { Q_D(MailboxList); d->mailboxList.append( mbox ); } void MailboxList::addAddress( const QByteArray &address, const QString &displayName ) { Q_D(MailboxList); Types::Mailbox mbox; if ( stringToMailbox( address, displayName, mbox ) ) { d->mailboxList.append( mbox ); } } QList< QByteArray > MailboxList::addresses() const { QList rv; foreach ( Types::Mailbox mbox, d_func()->mailboxList ) { rv.append( mbox.address() ); } return rv; } QStringList MailboxList::displayNames() const { QStringList rv; foreach ( Types::Mailbox mbox, d_func()->mailboxList ) { rv.append( mbox.name() ); } return rv; } QStringList MailboxList::prettyAddresses() const { QStringList rv; foreach ( Types::Mailbox mbox, d_func()->mailboxList ) { rv.append( mbox.prettyAddress() ); } return rv; } Types::Mailbox::List MailboxList::mailboxes() const { return d_func()->mailboxList; } bool MailboxList::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(MailboxList); // examples: // from := "From:" mailbox-list CRLF // sender := "Sender:" mailbox CRLF // parse an address-list: QList maybeAddressList; if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) { return false; } d->mailboxList.clear(); // extract the mailboxes and complain if there are groups: QList::Iterator it; for ( it = maybeAddressList.begin(); it != maybeAddressList.end() ; ++it ) { if ( !(*it).displayName.isEmpty() ) { KMIME_WARN << "mailbox groups in header disallowing them! Name: \"" << (*it).displayName << "\"" << endl; } d->mailboxList += (*it).mailboxList; } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( SingleMailbox, MailboxList ) //@endcond bool SingleMailbox::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(MailboxList); if ( !MailboxList::parse( scursor, send, isCRLF ) ) { return false; } if ( d->mailboxList.count() > 1 ) { KMIME_WARN << "multiple mailboxes in header allowing only a single one!" << endl; } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( AddressList, Address ) kmime_mk_dptr_ctor( AddressList, Address ) //@endcond QByteArray AddressList::as7BitString( bool withHeaderType ) const { const Q_D(AddressList); if ( d->addressList.isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv = typeIntro(); } foreach ( Types::Address addr, d->addressList ) { foreach ( Types::Mailbox mbox, addr.mailboxList ) { rv += mbox.as7BitString( d->encCS ); rv += ", "; } } rv.resize( rv.length() - 2 ); return rv; } void AddressList::fromUnicodeString( const QString &s, const QByteArray &b ) { Q_D(AddressList); d->encCS = cachedCharset( b ); from7BitString( encodeRFC2047String( s, b, false ) ); } QString AddressList::asUnicodeString() const { return prettyAddresses().join( QLatin1String( ", " ) ); } void AddressList::clear() { Q_D(AddressList); d->addressList.clear(); } bool AddressList::isEmpty() const { return d_func()->addressList.isEmpty(); } void AddressList::addAddress( const Types::Mailbox &mbox ) { Q_D(AddressList); Types::Address addr; addr.mailboxList.append( mbox ); d->addressList.append( addr ); } void AddressList::addAddress( const QByteArray &address, const QString &displayName ) { Q_D(AddressList); Types::Address addr; Types::Mailbox mbox; if ( stringToMailbox( address, displayName, mbox ) ) { addr.mailboxList.append( mbox ); d->addressList.append( addr ); } } QList< QByteArray > AddressList::addresses() const { QList rv; foreach ( Types::Address addr, d_func()->addressList ) { foreach ( Types::Mailbox mbox, addr.mailboxList ) { rv.append( mbox.address() ); } } return rv; } QStringList AddressList::displayNames() const { QStringList rv; foreach ( Types::Address addr, d_func()->addressList ) { foreach ( Types::Mailbox mbox, addr.mailboxList ) { rv.append( mbox.name() ); } } return rv; } QStringList AddressList::prettyAddresses() const { QStringList rv; foreach ( Types::Address addr, d_func()->addressList ) { foreach ( Types::Mailbox mbox, addr.mailboxList ) { rv.append( mbox.prettyAddress() ); } } return rv; } Types::Mailbox::List AddressList::mailboxes() const { Types::Mailbox::List rv; foreach ( Types::Address addr, d_func()->addressList ) { foreach ( Types::Mailbox mbox, addr.mailboxList ) { rv.append( mbox ); } } return rv; } bool AddressList::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(AddressList); QList maybeAddressList; if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) { return false; } d->addressList = maybeAddressList; return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( Token, Structured ) kmime_mk_dptr_ctor( Token, Structured ) //@endcond QByteArray Token::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } if ( withHeaderType ) { return typeIntro() + d_func()->token; } return d_func()->token; } void Token::clear() { Q_D(Token); d->token.clear(); } bool Token::isEmpty() const { return d_func()->token.isEmpty(); } QByteArray Token::token() const { return d_func()->token; } void Token::setToken( const QByteArray &t ) { Q_D(Token); d->token = t; } bool Token::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(Token); clear(); eatCFWS( scursor, send, isCRLF ); // must not be empty: if ( scursor == send ) { return false; } QPair maybeToken; if ( !parseToken( scursor, send, maybeToken, false /* no 8bit chars */ ) ) { return false; } d->token = QByteArray( maybeToken.first, maybeToken.second ); // complain if trailing garbage is found: eatCFWS( scursor, send, isCRLF ); if ( scursor != send ) { KMIME_WARN << "trailing garbage after token in header allowing " "only a single token!" << endl; } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( PhraseList, Structured ) //@endcond QByteArray PhraseList::as7BitString( bool withHeaderType ) const { const Q_D(PhraseList); if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv = typeIntro(); } for ( int i = 0; i < d->phraseList.count(); ++i ) { // FIXME: only encode when needed, quote when needed, etc. rv += encodeRFC2047String( d->phraseList[i], d->encCS, false, false ); if ( i != d->phraseList.count() - 1 ) { rv += ", "; } } return rv; } QString PhraseList::asUnicodeString() const { return d_func()->phraseList.join( QLatin1String( ", " ) ); } void PhraseList::clear() { Q_D(PhraseList); d->phraseList.clear(); } bool PhraseList::isEmpty() const { return d_func()->phraseList.isEmpty(); } QStringList PhraseList::phrases() const { return d_func()->phraseList; } bool PhraseList::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(PhraseList); d->phraseList.clear(); while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // empty entry ending the list: OK. if ( scursor == send ) { return true; } // empty entry: ignore. if ( *scursor == ',' ) { scursor++; continue; } QString maybePhrase; if ( !parsePhrase( scursor, send, maybePhrase, isCRLF ) ) { return false; } d->phraseList.append( maybePhrase ); eatCFWS( scursor, send, isCRLF ); // non-empty entry ending the list: OK. if ( scursor == send ) { return true; } // comma separating the phrases: eat. if ( *scursor == ',' ) { scursor++; } } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( DotAtom, Structured ) //@endcond QByteArray DotAtom::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } rv += d_func()->dotAtom.toLatin1(); // FIXME: encoding? return rv; } QString DotAtom::asUnicodeString() const { return d_func()->dotAtom; } void DotAtom::clear() { Q_D(DotAtom); d->dotAtom.clear(); } bool DotAtom::isEmpty() const { return d_func()->dotAtom.isEmpty(); } bool DotAtom::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(DotAtom); QString maybeDotAtom; if ( !parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) { return false; } d->dotAtom = maybeDotAtom; eatCFWS( scursor, send, isCRLF ); if ( scursor != send ) { KMIME_WARN << "trailing garbage after dot-atom in header allowing " "only a single dot-atom!" << endl; } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( Parametrized, Structured ) kmime_mk_dptr_ctor( Parametrized, Structured ) //@endcond QByteArray Parametrized::as7BitString( bool withHeaderType ) const { const Q_D(Parametrized); if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } bool first = true; for ( QMap::ConstIterator it = d->parameterHash.constBegin(); it != d->parameterHash.constEnd(); ++it ) { if ( !first ) { rv += "; "; } else { first = false; } rv += it.key().toLatin1() + '='; if ( isUsAscii( it.value() ) ) { QByteArray tmp = it.value().toLatin1(); addQuotes( tmp, true ); // force quoting, eg. for whitespaces in parameter value rv += tmp; } else { // FIXME: encoded strings are not allowed inside quotes, OTOH we need to quote whitespaces... rv += "\"" + encodeRFC2047String( it.value(), d->encCS ) + "\""; } } return rv; } QString Parametrized::parameter( const QString &key ) const { return d_func()->parameterHash.value( key ); } void Parametrized::setParameter( const QString &key, const QString &value ) { Q_D(Parametrized); d->parameterHash.insert( key, value ); } bool Parametrized::isEmpty() const { return d_func()->parameterHash.isEmpty(); } void Parametrized::clear() { Q_D(Parametrized); d->parameterHash.clear(); } bool Parametrized::parse( const char *& scursor, const char * const send, bool isCRLF ) { Q_D(Parametrized); d->parameterHash.clear(); if ( !parseParameterList( scursor, send, d->parameterHash, isCRLF ) ) { return false; } return true; } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( Ident, Address ) kmime_mk_dptr_ctor( Ident, Address ) //@endcond QByteArray Ident::as7BitString( bool withHeaderType ) const { const Q_D(Ident); if ( d->msgIdList.isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv = typeIntro(); } foreach ( Types::AddrSpec addr, d->msgIdList ) { rv += '<'; rv += addr.asString().toLatin1(); // FIXME: change parsing to use QByteArrays rv += "> "; } rv.resize( rv.length() - 1 ); return rv; } void Ident::clear() { Q_D(Ident); d->msgIdList.clear(); } bool Ident::isEmpty() const { return d_func()->msgIdList.isEmpty(); } bool Ident::parse( const char* &scursor, const char * const send, bool isCRLF ) { Q_D(Ident); // msg-id := "<" id-left "@" id-right ">" // id-left := dot-atom-text / no-fold-quote / local-part // id-right := dot-atom-text / no-fold-literal / domain // // equivalent to: // msg-id := angle-addr d->msgIdList.clear(); while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // empty entry ending the list: OK. if ( scursor == send ) { return true; } // empty entry: ignore. if ( *scursor == ',' ) { scursor++; continue; } AddrSpec maybeMsgId; if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) { return false; } d->msgIdList.append( maybeMsgId ); eatCFWS( scursor, send, isCRLF ); // header end ending the list: OK. if ( scursor == send ) { return true; } // regular item separator: eat it. if ( *scursor == ',' ) { scursor++; } } return true; } QList Ident::identifiers() const { QList rv; foreach ( Types::AddrSpec addr, d_func()->msgIdList ) { rv.append( addr.asString().toLatin1() ); // FIXME change parsing to create QByteArrays } return rv; } void Ident::appendIdentifier( const QByteArray &id ) { Q_D(Ident); QByteArray tmp = id; if ( !tmp.startsWith( '<' ) ) { tmp.prepend( '<' ); } if ( !tmp.endsWith( '>' ) ) { tmp.append( '>' ); } AddrSpec msgId; const char *cursor = tmp.constData(); if ( parseAngleAddr( cursor, cursor + tmp.length(), msgId ) ) { d->msgIdList.append( msgId ); } else { kWarning() << "Unable to parse address spec!"; } } //------------------------------ //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_dptr( SingleIdent, Ident ) //@endcond QByteArray SingleIdent::identifier() const { if ( d_func()->msgIdList.isEmpty() ) { return QByteArray(); } return identifiers().first(); } void SingleIdent::setIdentifier( const QByteArray &id ) { Q_D(SingleIdent); d->msgIdList.clear(); appendIdentifier( id ); } bool SingleIdent::parse( const char* &scursor, const char * const send, bool isCRLF ) { Q_D(SingleIdent); if ( !Ident::parse( scursor, send, isCRLF ) ) { return false; } if ( d->msgIdList.count() > 1 ) { KMIME_WARN << "more than one msg-id in header " << "allowing only a single one!" << endl; } return true; } //------------------------------ } // namespace Generics //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( ReturnPath, Generics::Address, Return-Path ) //@endcond QByteArray ReturnPath::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } rv += '<' + d_func()->mailbox.as7BitString( d_func()->encCS ) + '>'; return rv; } void ReturnPath::clear() { Q_D(ReturnPath); d->mailbox.setAddress( Types::AddrSpec() ); d->mailbox.setName( QString() ); } bool ReturnPath::isEmpty() const { const Q_D(ReturnPath); return !d->mailbox.hasAddress() && !d->mailbox.hasName(); } bool ReturnPath::parse( const char* &scursor, const char * const send, bool isCRLF ) { Q_D(ReturnPath); eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return false; } const char * oldscursor = scursor; Mailbox maybeMailbox; if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) { // mailbox parsing failed, but check for empty brackets: scursor = oldscursor; if ( *scursor != '<' ) { return false; } scursor++; eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '>' ) { return false; } scursor++; // prepare a Null mailbox: AddrSpec emptyAddrSpec; maybeMailbox.setName( QString() ); maybeMailbox.setAddress( emptyAddrSpec ); } else { // check that there was no display-name: if ( maybeMailbox.hasName() ) { KMIME_WARN << "display-name \"" << maybeMailbox.name() << "\" in Return-Path!" << endl; } } d->mailbox = maybeMailbox; // see if that was all: eatCFWS( scursor, send, isCRLF ); // and warn if it wasn't: if ( scursor != send ) { KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl; } return true; } //------------------------------ //------------------------------------ // NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable. Generic::Generic() : Generics::Unstructured( new GenericPrivate ) { } Generic::Generic( const char *t ) : Generics::Unstructured( new GenericPrivate ) { setType( t ); } Generic::Generic( const char *t, Content *p ) : Generics::Unstructured( new GenericPrivate, p ) { setType( t ); } Generic::Generic( const char *t, Content *p, const QByteArray &s ) : Generics::Unstructured( new GenericPrivate, p ) { from7BitString( s ); setType( t ); } Generic::Generic( const char *t, Content *p, const QString &s, const QByteArray &cs ) : Generics::Unstructured( new GenericPrivate, p ) { fromUnicodeString( s, cs ); setType( t ); } Generic::~Generic() { } void Generic::clear() { Q_D(Generic); delete[] d->type; d->type = 0; Unstructured::clear(); } -Base *Generic::clone() const -{ - Q_D( const Generic ); - Generic *ret = new Generic( d->type ); - ret->from7BitString( as7BitString( false ) ); - return ret; -} - bool Generic::isEmpty() const { return d_func()->type == 0 || Unstructured::isEmpty(); } const char *Generic::type() const { return d_func()->type; } void Generic::setType( const char *type ) { Q_D(Generic); if ( d->type ) { delete[] d->type; } if ( type ) { d->type = new char[strlen( type )+1]; strcpy( d->type, type ); } else { d->type = 0; } } //------------------------------------ //---------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name( MessageID, Generics::SingleIdent, Message-Id ) //@endcond void MessageID::generate( const QByteArray &fqdn ) { setIdentifier( uniqueString() + '@' + fqdn + '>' ); } //--------------------------------- //------------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( Control, Generics::Structured, Control ) //@endcond QByteArray Control::as7BitString( bool withHeaderType ) const { const Q_D(Control); if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } rv += d->name; if ( !d->parameter.isEmpty() ) { rv += ' ' + d->parameter; } return rv; } void Control::clear() { Q_D(Control); d->name.clear(); d->parameter.clear(); } bool Control::isEmpty() const { return d_func()->name.isEmpty(); } QByteArray Control::controlType() const { return d_func()->name; } QByteArray Control::parameter() const { return d_func()->parameter; } bool Control::isCancel() const { return d_func()->name.toLower() == "cancel"; } void Control::setCancel( const QByteArray &msgid ) { Q_D(Control); d->name = "cancel"; d->parameter = msgid; } bool Control::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(Control); clear(); eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return false; } const char *start = scursor; while ( scursor != send && !isspace( *scursor ) ) { ++scursor; } d->name = QByteArray( start, scursor - start ); eatCFWS( scursor, send, isCRLF ); d->parameter = QByteArray( scursor, send - scursor ); return true; } //----------------------------------- //------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( MailCopiesTo, Generics::AddressList, Mail-Copies-To ) //@endcond QByteArray MailCopiesTo::as7BitString( bool withHeaderType ) const { QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } if ( !AddressList::isEmpty() ) { rv += AddressList::as7BitString( false ); } else { if ( d_func()->alwaysCopy ) { rv += "poster"; } else if ( d_func()->neverCopy ) { rv += "nobody"; } } return rv; } QString MailCopiesTo::asUnicodeString() const { if ( !AddressList::isEmpty() ) { return AddressList::asUnicodeString(); } if ( d_func()->alwaysCopy ) { return QLatin1String( "poster" ); } if ( d_func()->neverCopy ) { return QLatin1String( "nobody" ); } return QString(); } void MailCopiesTo::clear() { Q_D(MailCopiesTo); AddressList::clear(); d->alwaysCopy = false; d->neverCopy = false; } bool MailCopiesTo::isEmpty() const { return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy); } bool MailCopiesTo::alwaysCopy() const { return !AddressList::isEmpty() || d_func()->alwaysCopy; } void MailCopiesTo::setAlwaysCopy() { Q_D(MailCopiesTo); clear(); d->alwaysCopy = true; } bool MailCopiesTo::neverCopy() const { return d_func()->neverCopy; } void MailCopiesTo::setNeverCopy() { Q_D(MailCopiesTo); clear(); d->neverCopy = true; } bool MailCopiesTo::parse( const char *& scursor, const char * const send, bool isCRLF ) { Q_D(MailCopiesTo); clear(); if ( send - scursor == 5 ) { if ( qstrnicmp( "never", scursor, 5 ) == 0 ) { d->neverCopy = true; return true; } } if ( send - scursor == 6 ) { if ( qstrnicmp( "always", scursor, 6 ) == 0 || qstrnicmp( "poster", scursor, 6 ) == 0 ) { d->alwaysCopy = true; return true; } if ( qstrnicmp( "nobody", scursor, 6 ) == 0 ) { d->alwaysCopy = true; return true; } } return AddressList::parse( scursor, send, isCRLF ); } //------------------------------ //--------------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( Date, Generics::Structured, Date ) //@endcond QByteArray Date::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } rv += d_func()->dateTime.toString( KDateTime::RFCDateDay ).toLatin1(); return rv; } void Date::clear() { Q_D(Date); d->dateTime = KDateTime(); } bool Date::isEmpty() const { return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid(); } KDateTime Date::dateTime() const { return d_func()->dateTime; } void Date::setDateTime( const KDateTime &dt ) { Q_D(Date); d->dateTime = dt; } int Date::ageInDays() const { QDate today = QDate::currentDate(); return dateTime().date().daysTo(today); } bool Date::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(Date); return parseDateTime( scursor, send, d->dateTime, isCRLF ); } //-------------------------------------- //--------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( Newsgroups, Generics::Structured, Newsgroups ) kmime_mk_trivial_ctor_with_name( FollowUpTo, Newsgroups, Followup-To ) //@endcond QByteArray Newsgroups::as7BitString( bool withHeaderType ) const { const Q_D(Newsgroups); if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } for ( int i = 0; i < d->groups.count(); ++i ) { rv += d->groups[ i ]; if ( i != d->groups.count() - 1 ) { rv += ','; } } return rv; } void Newsgroups::fromUnicodeString( const QString &s, const QByteArray &b ) { Q_UNUSED( b ); Q_D(Newsgroups); from7BitString( s.toUtf8() ); d->encCS = cachedCharset( "UTF-8" ); } QString Newsgroups::asUnicodeString() const { return QString::fromUtf8( as7BitString( false ) ); } void Newsgroups::clear() { Q_D(Newsgroups); d->groups.clear(); } bool Newsgroups::isEmpty() const { return d_func()->groups.isEmpty(); } QList Newsgroups::groups() const { return d_func()->groups; } void Newsgroups::setGroups( const QList &groups ) { Q_D(Newsgroups); d->groups = groups; } bool Newsgroups::isCrossposted() const { return d_func()->groups.count() >= 2; } bool Newsgroups::parse( const char* &scursor, const char *const send, bool isCRLF ) { Q_D(Newsgroups); clear(); forever { eatCFWS( scursor, send, isCRLF ); if ( scursor != send && *scursor == ',' ) { ++scursor; } eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return true; } const char *start = scursor; while ( scursor != send && !isspace( *scursor ) && *scursor != ',' ) { ++scursor; } QByteArray group( start, scursor - start ); d->groups.append( group ); } return true; } //-------------------------------- //-------------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( Lines, Generics::Structured, Lines ) //@endcond QByteArray Lines::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray num; num.setNum( d_func()->lines ); if ( withHeaderType ) { return typeIntro() + num; } return num; } QString Lines::asUnicodeString() const { if ( isEmpty() ) { return QString(); } return QString::number( d_func()->lines ); } void Lines::clear() { Q_D(Lines); d->lines = -1; } bool Lines::isEmpty() const { return d_func()->lines == -1; } int Lines::numberOfLines() const { return d_func()->lines; } void Lines::setNumberOfLines( int lines ) { Q_D(Lines); d->lines = lines; } bool Lines::parse( const char* &scursor, const char* const send, bool isCRLF ) { Q_D(Lines); eatCFWS( scursor, send, isCRLF ); if ( parseDigits( scursor, send, d->lines ) == 0 ) { clear(); return false; } return true; } //------------------------------------- //------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( ContentType, Generics::Parametrized, Content-Type ) //@endcond bool ContentType::isEmpty() const { return d_func()->mimeType.isEmpty(); } void ContentType::clear() { Q_D(ContentType); d->category = CCsingle; d->mimeType.clear(); d->mimeSubType.clear(); Parametrized::clear(); } QByteArray ContentType::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } rv += mimeType(); if ( !Parametrized::isEmpty() ) { rv += "; " + Parametrized::as7BitString( false ); } return rv; } QByteArray ContentType::mimeType() const { return d_func()->mimeType + '/' + d_func()->mimeSubType; } QByteArray ContentType::mediaType() const { return d_func()->mimeType; } QByteArray ContentType::subType() const { return d_func()->mimeSubType; } void ContentType::setMimeType( const QByteArray &mimeType ) { Q_D(ContentType); int pos = mimeType.indexOf( '/' ); if ( pos < 0 ) { d->mimeType = mimeType; d->mimeSubType.clear(); } else { d->mimeType = mimeType.left( pos ); d->mimeSubType = mimeType.mid( pos + 1 ); } Parametrized::clear(); if ( isMultipart() ) { d->category = CCcontainer; } else { d->category = CCsingle; } } bool ContentType::isMediatype( const char *mediatype ) const { return strncasecmp( mediaType().constData(), mediatype, strlen( mediatype ) ) == 0; } bool ContentType::isSubtype( const char *subtype ) const { return strncasecmp( subType().constData(), subtype, strlen( subtype ) ) == 0; } bool ContentType::isText() const { return ( strncasecmp( mediaType().constData(), "text", 4 ) == 0 || isEmpty() ); } bool ContentType::isPlainText() const { return ( strcasecmp( mimeType().constData(), "text/plain" ) == 0 || isEmpty() ); } bool ContentType::isHTMLText() const { return strcasecmp( mimeType().constData(), "text/html" ) == 0; } bool ContentType::isImage() const { return strncasecmp( mediaType().constData(), "image", 5 ) == 0; } bool ContentType::isMultipart() const { return strncasecmp( mediaType().constData(), "multipart", 9 ) == 0; } bool ContentType::isPartial() const { return strcasecmp( mimeType().constData(), "message/partial" ) == 0; } QByteArray ContentType::charset() const { QByteArray ret = parameter( "charset" ).toLatin1(); if ( ret.isEmpty() || forceDefaultCharset() ) { //return the default-charset if necessary ret = defaultCharset(); } return ret; } void ContentType::setCharset( const QByteArray &s ) { setParameter( "charset", QString::fromLatin1( s ) ); } QByteArray ContentType::boundary() const { return parameter( "boundary" ).toLatin1(); } void ContentType::setBoundary( const QByteArray &s ) { setParameter( "boundary", QString::fromLatin1( s ) ); } QString ContentType::name() const { return parameter( "name" ); } void ContentType::setName( const QString &s, const QByteArray &cs ) { Q_D(ContentType); d->encCS = cs; setParameter( "name", s ); } QByteArray ContentType::id() const { return parameter( "id" ).toLatin1(); } void ContentType::setId( const QByteArray &s ) { setParameter( "id", s ); } int ContentType::partialNumber() const { QByteArray p = parameter( "number" ).toLatin1(); if ( !p.isEmpty() ) { return p.toInt(); } else { return -1; } } int ContentType::partialCount() const { QByteArray p = parameter( "total" ).toLatin1(); if ( !p.isEmpty() ) { return p.toInt(); } else { return -1; } } contentCategory ContentType::category() const { return d_func()->category; } void ContentType::setCategory( contentCategory c ) { Q_D(ContentType); d->category = c; } void ContentType::setPartialParams( int total, int number ) { setParameter( "number", QString::number( number ) ); setParameter( "total", QString::number( total ) ); } bool ContentType::parse( const char* &scursor, const char * const send, bool isCRLF ) { Q_D(ContentType); // content-type: type "/" subtype *(";" parameter) clear(); eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return false; // empty header } // type QPair maybeMimeType; if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) { return false; } d->mimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower(); // subtype eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '/' ) { return false; } scursor++; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return false; } QPair maybeSubType; if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) { return false; } d->mimeSubType = QByteArray( maybeSubType.first, maybeSubType.second ).toLower(); // parameter list eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { goto success; // no parameters } if ( *scursor != ';' ) { return false; } scursor++; if ( !Parametrized::parse( scursor, send, isCRLF ) ) { return false; } // adjust category success: if ( isMultipart() ) { d->category = CCcontainer; } else { d->category = CCsingle; } return true; } //------------------------------ //--------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( ContentTransferEncoding, Generics::Token, Content-Transfer-Encoding ) //@endcond typedef struct { const char *s; int e; } encTableType; static const encTableType encTable[] = { { "7Bit", CE7Bit }, { "8Bit", CE8Bit }, { "quoted-printable", CEquPr }, { "base64", CEbase64 }, { "x-uuencode", CEuuenc }, { "binary", CEbinary }, { 0, 0} }; void ContentTransferEncoding::clear() { Q_D(ContentTransferEncoding); d->decoded = true; d->cte = CE7Bit; Token::clear(); } contentEncoding ContentTransferEncoding::encoding() const { return d_func()->cte; } void ContentTransferEncoding::setEncoding( contentEncoding e ) { Q_D(ContentTransferEncoding); d->cte = e; for ( int i = 0; encTable[i].s != 0; ++i ) { if ( d->cte == encTable[i].e ) { setToken( encTable[i].s ); break; } } } bool ContentTransferEncoding::decoded() const { return d_func()->decoded; } void ContentTransferEncoding::setDecoded( bool decoded ) { Q_D(ContentTransferEncoding); d->decoded = decoded; } bool ContentTransferEncoding::needToEncode() const { const Q_D(ContentTransferEncoding); return d->decoded && (d->cte == CEquPr || d->cte == CEbase64); } bool ContentTransferEncoding::parse( const char *& scursor, const char * const send, bool isCRLF ) { Q_D(ContentTransferEncoding); clear(); if ( !Token::parse( scursor, send, isCRLF ) ) { return false; } // TODO: error handling in case of an unknown encoding? for ( int i = 0; encTable[i].s != 0; ++i ) { if ( strcasecmp( token().constData(), encTable[i].s ) == 0 ) { d->cte = ( contentEncoding )encTable[i].e; break; } } d->decoded = ( d->cte == CE7Bit || d->cte == CE8Bit ); return true; } //-------------------------------- //------------------------------- //@cond PRIVATE kmime_mk_trivial_ctor_with_name_and_dptr( ContentDisposition, Generics::Parametrized, Content-Disposition ) //@endcond QByteArray ContentDisposition::as7BitString( bool withHeaderType ) const { if ( isEmpty() ) { return QByteArray(); } QByteArray rv; if ( withHeaderType ) { rv += typeIntro(); } if ( d_func()->disposition == CDattachment ) { rv += "attachment"; } else if ( d_func()->disposition == CDinline ) { rv += "inline"; } else { return QByteArray(); } if ( !Parametrized::isEmpty() ) { rv += "; " + Parametrized::as7BitString( false ); } return rv; } bool ContentDisposition::isEmpty() const { return d_func()->disposition == CDInvalid; } void ContentDisposition::clear() { Q_D(ContentDisposition); d->disposition = CDInvalid; Parametrized::clear(); } contentDisposition ContentDisposition::disposition() const { return d_func()->disposition; } void ContentDisposition::setDisposition( contentDisposition disp ) { Q_D(ContentDisposition); d->disposition = disp; } QString KMime::Headers::ContentDisposition::filename() const { return parameter( "filename" ); } void ContentDisposition::setFilename( const QString &filename ) { setParameter( "filename", filename ); } bool ContentDisposition::parse( const char *& scursor, const char * const send, bool isCRLF ) { Q_D(ContentDisposition); clear(); // token QByteArray token; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return false; } QPair maybeToken; if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) { return false; } token = QByteArray( maybeToken.first, maybeToken.second ).toLower(); if ( token == "inline" ) { d->disposition = CDinline; } else if ( token == "attachment" ) { d->disposition = CDattachment; } else { return false; } // parameter list eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { return true; // no parameters } if ( *scursor != ';' ) { return false; } scursor++; return Parametrized::parse( scursor, send, isCRLF ); } //------------------------------ //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Subject, Generics::Unstructured, Subject ) //@endcond bool Subject::isReply() const { return asUnicodeString().indexOf( QLatin1String( "Re:" ), 0, Qt::CaseInsensitive ) == 0; } //@cond PRIVATE kmime_mk_trivial_ctor_with_name( ContentDescription, Generics::Unstructured, Content-Description ) kmime_mk_trivial_ctor_with_name( ContentLocation, Generics::Unstructured, Content-Location ) kmime_mk_trivial_ctor_with_name( From, Generics::MailboxList, From ) kmime_mk_trivial_ctor_with_name( Sender, Generics::SingleMailbox, Sender ) kmime_mk_trivial_ctor_with_name( To, Generics::AddressList, To ) kmime_mk_trivial_ctor_with_name( Cc, Generics::AddressList, Cc ) kmime_mk_trivial_ctor_with_name( Bcc, Generics::AddressList, Bcc ) kmime_mk_trivial_ctor_with_name( ReplyTo, Generics::AddressList, Reply-To ) kmime_mk_trivial_ctor_with_name( Keywords, Generics::PhraseList, Keywords ) kmime_mk_trivial_ctor_with_name( MIMEVersion, Generics::DotAtom, MIME-Version ) kmime_mk_trivial_ctor_with_name( ContentID, Generics::SingleIdent, Content-ID ) kmime_mk_trivial_ctor_with_name( Supersedes, Generics::SingleIdent, Supersedes ) kmime_mk_trivial_ctor_with_name( InReplyTo, Generics::Ident, In-Reply-To ) kmime_mk_trivial_ctor_with_name( References, Generics::Ident, References ) kmime_mk_trivial_ctor_with_name( Organization, Generics::Unstructured, Organization ) kmime_mk_trivial_ctor_with_name( UserAgent, Generics::Unstructured, User-Agent ) //@endcond } // namespace Headers } // namespace KMime diff --git a/kmime/kmime_headers.h b/kmime/kmime_headers.h index 03a0427af..4569f4c39 100644 --- a/kmime/kmime_headers.h +++ b/kmime/kmime_headers.h @@ -1,1488 +1,1479 @@ /* -*- c++ -*- kmime_headers.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 the KMime authors. See file AUTHORS for details Copyright (c) 2006 Volker Krause 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 various header classes: - header's base class defining the common interface - generic base classes for different types of fields - incompatible, Structured-based field classes - compatible, Unstructured-based field classes @brief Defines the various headers classes. @authors the KMime authors (see AUTHORS file), Volker Krause \ */ #ifndef __KMIME_HEADERS_H__ #define __KMIME_HEADERS_H__ #include "kmime_export.h" #include "kmime_header_parsing.h" #include #include #include #include #include #include #include #include namespace KMime { class Content; namespace Headers { class BasePrivate; enum contentCategory { CCsingle, CCcontainer, CCmixedPart, CCalternativePart }; /** Various possible values for the "Content-Transfer-Encoding" header. */ enum contentEncoding { CE7Bit, ///< 7bit CE8Bit, ///< 8bit CEquPr, ///< quoted-printable CEbase64, ///< base64 CEuuenc, ///< uuencode CEbinary ///< binary }; /** Various possible values for the "Content-Disposition" header. */ enum contentDisposition { CDInvalid, ///< Default, invalid value CDinline, ///< inline CDattachment, ///< attachment CDparallel ///< parallel (invalid, do not use) }; //often used charset // TODO: get rid of this! static const QByteArray Latin1( "ISO-8859-1" ); //@cond PRIVATE // internal macro to generate default constructors #define kmime_mk_trivial_ctor( subclass ) \ public: \ explicit subclass( Content *parent = 0 ); \ subclass( Content *parent, const QByteArray &s ); \ subclass( Content *parent, const QString &s, const QByteArray &charset ); \ - ~subclass(); \ - virtual Base *clone() const; + ~subclass(); #define kmime_mk_dptr_ctor( subclass ) \ protected: \ explicit subclass( subclass##Private *d, KMime::Content *parent = 0 ); #define kmime_mk_trivial_ctor_with_name( subclass ) \ kmime_mk_trivial_ctor( subclass ) \ const char *type() const; //@endcond // // // HEADER'S BASE CLASS. DEFINES THE COMMON INTERFACE // // /** Baseclass of all header-classes. It represents a header-field as described in RFC-822. */ class KMIME_EXPORT Base { public: /** A list of headers. */ typedef QList List; /** Creates an empty header with a parent-content. */ explicit Base( KMime::Content *parent = 0 ); /** Destructor. */ virtual ~Base(); /** Returns the parent of this header. */ KMime::Content *parent() const; /** Sets the parent for this header to @p parent. */ void setParent( KMime::Content *parent ); /** Parses the given string. Take care of RFC2047-encoded strings. @param s The encoded header data. */ virtual void from7BitString( const QByteArray &s ) = 0; /** Returns the encoded header. @param withHeaderType Specifies whether the header-type should be included. */ virtual QByteArray as7BitString( bool withHeaderType = true ) const = 0; /** Returns the charset that is used for RFC2047-encoding. */ QByteArray rfc2047Charset() const; /** Sets the charset for RFC2047-encoding. @param cs The new charset used for RFC2047 encoding. */ void setRFC2047Charset( const QByteArray &cs ); /** Returns the default charset. */ QByteArray defaultCharset() const; /** Returns if the default charset is mandatory. */ bool forceDefaultCharset() const; /** Parses the given string and set the charset. @param s The header data as unicode string. @param b The charset preferred for encoding. */ virtual void fromUnicodeString( const QString &s, const QByteArray &b ) = 0; /** Returns the decoded content of the header without the header-type. */ virtual QString asUnicodeString() const = 0; /** Deletes. */ virtual void clear() = 0; - /** - Makes a copy of this header. - This function does not set the parent of the header it returns. - */ - virtual Base *clone() const = 0; - /** Checks if this header contains any data. */ virtual bool isEmpty() const = 0; /** Returns the type of this header (e.g. "From"). */ virtual const char *type() const; /** Checks if this header is of type @p t. */ bool is( const char *t ) const; /** Checks if this header is a MIME header. */ bool isMimeHeader() const; /** Checks if this header is a X-Header. */ bool isXHeader() const; protected: /** Helper method, returns the header prefix including ":". */ QByteArray typeIntro() const; //@cond PRIVATE BasePrivate *d_ptr; kmime_mk_dptr_ctor( Base ) //@endcond private: Q_DECLARE_PRIVATE(Base) Q_DISABLE_COPY(Base) }; // // // GENERIC BASE CLASSES FOR DIFFERENT TYPES OF FIELDS // // namespace Generics { class UnstructuredPrivate; /** Abstract base class for unstructured header fields (e.g. "Subject", "Comment", "Content-description"). Features: Decodes the header according to RFC2047, incl. RFC2231 extensions to encoded-words. Subclasses need only re-implement @p const @p char* @p type(). */ // known issues: // - uses old decodeRFC2047String function, instead of our own... class KMIME_EXPORT Unstructured : public Base { //@cond PRIVATE kmime_mk_dptr_ctor( Unstructured ) //@endcond public: explicit Unstructured( Content *p = 0 ); Unstructured( Content *p, const QByteArray &s ); Unstructured( Content *p, const QString &s, const QByteArray &cs ); ~Unstructured(); virtual void from7BitString( const QByteArray &s ); virtual QByteArray as7BitString( bool withHeaderType=true ) const; virtual void fromUnicodeString( const QString &s, const QByteArray &b ); virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; private: Q_DECLARE_PRIVATE(Unstructured) }; class StructuredPrivate; /** @brief Base class for structured header fields. This is the base class for all structured header fields. It contains parsing methods for all basic token types found in rfc2822. @section Parsing At the basic level, there are tokens & tspecials (rfc2045), atoms & specials, quoted-strings, domain-literals (all rfc822) and encoded-words (rfc2047). As a special token, we have the comment. It is one of the basic tokens defined in rfc822, but it's parsing relies in part on the basic token parsers (e.g. comments may contain encoded-words). Also, most upper-level parsers (notably those for phrase and dot-atom) choose to ignore any comment when parsing. Then there are the real composite tokens, which are made up of one or more of the basic tokens (and semantically invisible comments): phrases (rfc822 with rfc2047) and dot-atoms (rfc2822). This finishes the list of supported token types. Subclasses will provide support for more higher-level tokens, where necessary, using these parsers. @author Marc Mutz */ class KMIME_EXPORT Structured : public Base { public: explicit Structured( Content *p = 0 ); Structured( Content *p, const QByteArray &s ); Structured( Content *p, const QString &s, const QByteArray &cs ); ~Structured(); virtual void from7BitString( const QByteArray &s ); virtual QString asUnicodeString() const; virtual void fromUnicodeString( const QString &s, const QByteArray &b ); protected: /** This method parses the raw header and needs to be implemented in every sub-class. @param scursor Pointer to the start of the data still to parse. @param send Pointer to the end of the data. @param isCRLF true if input string is terminated with a CRLF. */ virtual bool parse( const char* &scursor, const char *const send, bool isCRLF = false ) = 0; //@cond PRIVATE kmime_mk_dptr_ctor( Structured ) //@endcond private: Q_DECLARE_PRIVATE(Structured) }; class AddressPrivate; /** Base class for all address related headers. */ class KMIME_EXPORT Address : public Structured { public: explicit Address( Content *p = 0 ); Address( Content *p, const QByteArray &s ); Address( Content *p, const QString &s, const QByteArray &cs ); ~Address(); protected: //@cond PRIVATE kmime_mk_dptr_ctor( Address ) //@endcond private: Q_DECLARE_PRIVATE(Address) }; class MailboxListPrivate; /** Base class for headers that deal with (possibly multiple) addresses, but don't allow groups. @see RFC 2822, section 3.4 */ class KMIME_EXPORT MailboxList : public Address { //@cond PRIVATE kmime_mk_trivial_ctor( MailboxList ) kmime_mk_dptr_ctor( MailboxList ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void fromUnicodeString( const QString &s, const QByteArray &b ); virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Adds an address to this header. @param mbox A Mailbox object specifying the address. */ void addAddress( const Types::Mailbox &mbox ); /** Adds an address to this header. @param address The actual email address, with or without angle brackets. @param displayName An optional name associated with the address. */ void addAddress( const QByteArray &address, const QString &displayName = QString() ); /** Returns a list of all addresses in this header, regardless of groups. */ QList addresses() const; /** Returns a list of all display names associated with the addresses in this header. An empty entry is added for addresses that do not have a display name. */ QStringList displayNames() const; /** Returns a list of assembled display name / address strings of the following form: "Display Name <address>". These are unicode strings without any transport encoding, ie. they are only suitable for displaying. */ QStringList prettyAddresses() const; /** Returns a list of mailboxes listed in this header. */ Types::Mailbox::List mailboxes() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(MailboxList) }; class SingleMailboxPrivate; /** Base class for headers that deal with exactly one mailbox (e.g. Sender). */ class KMIME_EXPORT SingleMailbox : public MailboxList { //@cond PRIVATE kmime_mk_trivial_ctor( SingleMailbox ) //@endcond protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(SingleMailbox) }; class AddressListPrivate; /** Base class for headers that deal with (possibly multiple) addresses, allowing groups. Note: Groups are parsed but not represented in the API yet. All addresses in groups are listed as if they would not be part of a group. @todo Add API for groups? @see RFC 2822, section 3.4 */ class KMIME_EXPORT AddressList : public Address { //@cond PRIVATE kmime_mk_trivial_ctor( AddressList ) kmime_mk_dptr_ctor( AddressList ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void fromUnicodeString( const QString &s, const QByteArray &b ); virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Adds an address to this header. @param mbox A Mailbox object specifying the address. */ void addAddress( const Types::Mailbox &mbox ); /** Adds an address to this header. @param address The actual email address, with or without angle brackets. @param displayName An optional name associated with the address. */ void addAddress( const QByteArray &address, const QString &displayName = QString() ); /** Returns a list of all addresses in this header, regardless of groups. */ QList addresses() const; /** Returns a list of all display names associated with the addresses in this header. An empty entry is added for addresses that don't have a display name. */ QStringList displayNames() const; /** Returns a list of assembled display name / address strings of the following form: "Display Name <address>". These are unicode strings without any transport encoding, ie. they are only suitable for displaying. */ QStringList prettyAddresses() const; /** Returns a list of mailboxes listed in this header. */ Types::Mailbox::List mailboxes() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(AddressList) }; class IdentPrivate; /** Base class for headers which deal with a list of msg-id's. @see RFC 2822, section 3.6.4 */ class KMIME_EXPORT Ident : public Address { //@cond PRIVATE kmime_mk_trivial_ctor( Ident ) kmime_mk_dptr_ctor( Ident ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; /** Returns the list of identifiers contained in this header. Note: - Identifiers are not enclosed in angle-brackets. - Identifiers are listed in the same order as in the header. */ QList identifiers() const; /** Appends a new identifier to this header. @param id The identifier to append, with or without angle-brackets. */ void appendIdentifier( const QByteArray &id ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(Ident) }; class SingleIdentPrivate; /** Base class for headers which deal with a single msg-id. @see RFC 2822, section 3.6.4 */ class KMIME_EXPORT SingleIdent : public Ident { //@cond PRIVATE kmime_mk_trivial_ctor( SingleIdent ) //@endcond public: /** Returns the identifier contained in this header. Note: The identifiers is not enclosed in angle-brackets. */ QByteArray identifier() const; /** Sets the identifier. @param id The new identifier with or without angle-brackets. */ void setIdentifier( const QByteArray &id ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(SingleIdent) }; class TokenPrivate; /** Base class for headers which deal with a single atom. */ class KMIME_EXPORT Token : public Structured { //@cond PRIVATE kmime_mk_trivial_ctor( Token ) kmime_mk_dptr_ctor( Token ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; /** Returns the token. */ QByteArray token() const; /** Sets the token to @p t, */ void setToken( const QByteArray &t ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(Token) }; class PhraseListPrivate; /** Base class for headers containing a list of phrases. */ class KMIME_EXPORT PhraseList : public Structured { //@cond PRIVATE kmime_mk_trivial_ctor( PhraseList ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Returns the list of phrases contained in this header. */ QStringList phrases() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(PhraseList) }; class DotAtomPrivate; /** Base class for headers containing a dot atom. */ class KMIME_EXPORT DotAtom : public Structured { //@cond PRIVATE kmime_mk_trivial_ctor( DotAtom ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(DotAtom) }; class ParametrizedPrivate; /** Base class for headers containing a parameter list such as "Content-Type". */ class KMIME_EXPORT Parametrized : public Structured { //@cond PRIVATE kmime_mk_trivial_ctor( Parametrized ) kmime_mk_dptr_ctor( Parametrized ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual bool isEmpty() const; virtual void clear(); /** Returns the value of the specified parameter. @param key The parameter name. */ QString parameter( const QString &key ) const; /** Sets the parameter @p key to @p value. @param key The parameter name. @param value The new value for @p key. */ void setParameter( const QString &key, const QString &value ); protected: virtual bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(Parametrized) }; } // namespace Generics // // // INCOMPATIBLE, GSTRUCTURED-BASED FIELDS: // // class ReturnPathPrivate; /** Represents the Return-Path header field. @see RFC 2822, section 3.6.7 */ class KMIME_EXPORT ReturnPath : public Generics::Address { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( ReturnPath ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(ReturnPath) }; // Address et al.: // rfc(2)822 headers: /** Represent a "From" header. @see RFC 2822, section 3.6.2. */ class KMIME_EXPORT From : public Generics::MailboxList { kmime_mk_trivial_ctor_with_name( From ) }; /** Represents a "Sender" header. @see RFC 2822, section 3.6.2. */ class KMIME_EXPORT Sender : public Generics::SingleMailbox { kmime_mk_trivial_ctor_with_name( Sender ) }; /** Represents a "To" header. @see RFC 2822, section 3.6.3. */ class KMIME_EXPORT To : public Generics::AddressList { kmime_mk_trivial_ctor_with_name( To ) }; /** Represents a "Cc" header. @see RFC 2822, section 3.6.3. */ class KMIME_EXPORT Cc : public Generics::AddressList { kmime_mk_trivial_ctor_with_name( Cc ) }; /** Represents a "Bcc" header. @see RFC 2822, section 3.6.3. */ class KMIME_EXPORT Bcc : public Generics::AddressList { kmime_mk_trivial_ctor_with_name( Bcc ) }; /** Represents a "ReplyTo" header. @see RFC 2822, section 3.6.2. */ class KMIME_EXPORT ReplyTo : public Generics::AddressList { kmime_mk_trivial_ctor_with_name( ReplyTo ) }; class MailCopiesToPrivate; /** Represents a "Mail-Copies-To" header. @see http://www.newsreaders.com/misc/mail-copies-to.html */ class KMIME_EXPORT MailCopiesTo : public Generics::AddressList { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( MailCopiesTo ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Returns true if a mail copy was explicitly requested. */ bool alwaysCopy() const; /** Sets the header to "poster". */ void setAlwaysCopy(); /** Returns true if a mail copy was explicitly denied. */ bool neverCopy() const; /** Sets the header to "never". */ void setNeverCopy(); protected: virtual bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(MailCopiesTo) }; class ContentTransferEncodingPrivate; /** Represents a "Content-Transfer-Encoding" header. @see RFC 2045, section 6. */ class KMIME_EXPORT ContentTransferEncoding : public Generics::Token { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( ContentTransferEncoding ) //@endcond public: virtual void clear(); /** Returns the encoding specified in this header. */ contentEncoding encoding() const; /** Sets the encoding to @p e. */ void setEncoding( contentEncoding e ); /** Returns whether the Content containing this header is already decoded. */ // KDE5: rename to isDecoded(). bool decoded() const; /** Set whether the Content containing this header is already decoded. For instance, if you fill your Content with already-encoded base64 data, you will want to setDecoded( false ). */ void setDecoded( bool decoded = true ); /** Returns whether the Content containing this header needs to be encoded (i.e., if decoded() is true and encoding() is base64 or quoted-printable). */ bool needToEncode() const; protected: virtual bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(ContentTransferEncoding) }; /** Represents a "Keywords" header. @see RFC 2822, section 3.6.5. */ class KMIME_EXPORT Keywords : public Generics::PhraseList { kmime_mk_trivial_ctor_with_name( Keywords ) }; // DotAtom: /** Represents a "MIME-Version" header. @see RFC 2045, section 4. */ class KMIME_EXPORT MIMEVersion : public Generics::DotAtom { kmime_mk_trivial_ctor_with_name( MIMEVersion ) }; // Ident: /** Represents a "Message-ID" header. @see RFC 2822, section 3.6.4. */ class KMIME_EXPORT MessageID : public Generics::SingleIdent { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( MessageID ) //@endcond public: /** Generate a message identifer. @param fqdn A fully qualified domain name. */ void generate( const QByteArray &fqdn ); }; /** Represents a "Content-ID" header. */ class KMIME_EXPORT ContentID : public Generics::SingleIdent { kmime_mk_trivial_ctor_with_name( ContentID ) }; /** Represents a "Supersedes" header. */ class KMIME_EXPORT Supersedes : public Generics::SingleIdent { kmime_mk_trivial_ctor_with_name( Supersedes ) }; /** Represents a "In-Reply-To" header. @see RFC 2822, section 3.6.4. */ class KMIME_EXPORT InReplyTo : public Generics::Ident { kmime_mk_trivial_ctor_with_name( InReplyTo ) }; /** Represents a "References" header. @see RFC 2822, section 3.6.4. */ class KMIME_EXPORT References : public Generics::Ident { kmime_mk_trivial_ctor_with_name( References ) }; class ContentTypePrivate; /** Represents a "Content-Type" header. @see RFC 2045, section 5. */ class KMIME_EXPORT ContentType : public Generics::Parametrized { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( ContentType ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; /** Returns the mimetype. */ QByteArray mimeType() const; /** Returns the media type (first part of the mimetype). */ QByteArray mediaType() const; /** Returns the mime sub-type (second part of the mimetype). */ QByteArray subType() const; /** Sets the mimetype and clears already existing parameters. @param mimeType The new mimetype. */ void setMimeType( const QByteArray &mimeType ); /** Tests if the media type equals @p mediatype. */ bool isMediatype( const char *mediatype ) const; /** Tests if the mime sub-type equals @p subtype. */ bool isSubtype( const char *subtype ) const; /** Returns true if the associated MIME entity is a text. */ bool isText() const; /** Returns true if the associated MIME entity is a plain text. */ bool isPlainText() const; /** Returns true if the associated MIME entity is a HTML file. */ bool isHTMLText() const; /** Returns true if the associated MIME entity is an image. */ bool isImage() const; /** Returns true if the associated MIME entity is a mulitpart container. */ bool isMultipart() const; /** Returns true if the associated MIME entity contains partial data. @see partialNumber(), partialCount() */ bool isPartial() const; /** Returns the charset for the associated MIME entity. */ QByteArray charset() const; /** Sets the charset. */ void setCharset( const QByteArray &s ); /** Returns the boundary (for mulitpart containers). */ QByteArray boundary() const; /** Sets the mulitpart container boundary. */ void setBoundary( const QByteArray &s ); /** Returns the name of the associated MIME entity. */ QString name() const; /** Sets the name to @p s using charset @p cs. */ void setName( const QString &s, const QByteArray &cs ); /** Returns the identifier of the associated MIME entity. */ QByteArray id() const; /** Sets the identifier. */ void setId( const QByteArray &s ); /** Returns the position of this part in a multi-part set. @see isPartial(), partialCount() */ int partialNumber() const; /** Returns the total number of parts in a multi-part set. @see isPartial(), partialNumber() */ int partialCount() const; /** Sets parameters of a partial MIME entity. @param total The total number of entities in the multi-part set. @param number The number of this entity in a multi-part set. */ void setPartialParams( int total, int number ); // TODO: document contentCategory category() const; void setCategory( contentCategory c ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE(ContentType) }; class ContentDispositionPrivate; /** Represents a "Content-Disposition" header. @see RFC 2183 */ class KMIME_EXPORT ContentDisposition : public Generics::Parametrized { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( ContentDisposition ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual bool isEmpty() const; virtual void clear(); /** Returns the content disposition. */ contentDisposition disposition() const; /** Sets the content disposition. @param disp The new content disposition. */ void setDisposition( contentDisposition disp ); /** Returns the suggested filename for the associated MIME part. This is just a convenience function, it is equivalent to calling parameter( "filename" ); */ QString filename() const; /** Sets the suggested filename for the associated MIME part. This is just a convenience function, it is equivalent to calling setParameter( "filename", filename ); @param filename The filename. */ void setFilename( const QString &filename ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF=false ); private: Q_DECLARE_PRIVATE( ContentDisposition ) }; // // // COMPATIBLE GUNSTRUCTURED-BASED FIELDS: // // class GenericPrivate; /** Represents an arbitrary header, that can contain any header-field. Adds a type over Unstructured. @see Unstructured */ class KMIME_EXPORT Generic : public Generics::Unstructured { public: Generic(); Generic( const char *t ); Generic( const char *t, Content *p ); Generic( const char *t, Content *p, const QByteArray &s ); Generic( const char *t, Content *p, const QString &s, const QByteArray &cs ); ~Generic(); virtual void clear(); - virtual Base *clone() const; - virtual bool isEmpty() const; virtual const char *type() const; void setType( const char *type ); private: Q_DECLARE_PRIVATE( Generic ) }; /** Represents a "Subject" header. @see RFC 2822, section 3.6.5. */ class KMIME_EXPORT Subject : public Generics::Unstructured { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Subject ) //@endcond public: bool isReply() const; }; /** Represents a "Organization" header. */ class KMIME_EXPORT Organization : public Generics::Unstructured { kmime_mk_trivial_ctor_with_name( Organization ) }; /** Represents a "Content-Description" header. */ class KMIME_EXPORT ContentDescription : public Generics::Unstructured { kmime_mk_trivial_ctor_with_name( ContentDescription ) }; /** Represents a "Content-Location" header. @since 4.2 */ class KMIME_EXPORT ContentLocation : public Generics::Unstructured { kmime_mk_trivial_ctor_with_name( ContentLocation ) }; class ControlPrivate; /** Represents a "Control" header. @see RFC 1036, section 3. */ class KMIME_EXPORT Control : public Generics::Structured { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Control ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; /** Returns the control message type. */ QByteArray controlType() const; /** Returns the control message parameter. */ QByteArray parameter() const; /** Returns true if this is a cancel control message. @see RFC 1036, section 3.1. */ bool isCancel() const; /** Changes this header into a cancel control message for the given message-id. @param msgid The message-id of the article that should be canceled. */ void setCancel( const QByteArray &msgid ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF = false ); private: Q_DECLARE_PRIVATE(Control) }; class DatePrivate; /** Represents a "Date" header. @see RFC 2822, section 3.3. */ class KMIME_EXPORT Date : public Generics::Structured { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Date ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void clear(); virtual bool isEmpty() const; /** Returns the date contained in this header. */ KDateTime dateTime() const; /** Sets the date. */ void setDateTime( const KDateTime &dt ); /** Returns the age of the message. */ int ageInDays() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF = false ); private: Q_DECLARE_PRIVATE( Date ) }; class NewsgroupsPrivate; /** Represents a "Newsgroups" header. @see RFC 1036, section 2.1.3. */ class KMIME_EXPORT Newsgroups : public Generics::Structured { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Newsgroups ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual void fromUnicodeString( const QString &s, const QByteArray &b ); virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Returns the list of newsgroups. */ QList groups() const; /** Sets the newsgroup list. */ void setGroups( const QList &groups ); /** Returns true if this message has been cross-posted, i.e. if it has been posted to multiple groups. */ bool isCrossposted() const; protected: bool parse( const char* &scursor, const char *const send, bool isCRLF = false ); private: Q_DECLARE_PRIVATE( Newsgroups ) }; /** Represents a "Followup-To" header. @see RFC 1036, section 2.2.3. */ class KMIME_EXPORT FollowUpTo : public Newsgroups { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( FollowUpTo ) //@endcond }; class LinesPrivate; /** Represents a "Lines" header. @see RFC 1036, section 2.2.12. */ class KMIME_EXPORT Lines : public Generics::Structured { //@cond PRIVATE kmime_mk_trivial_ctor_with_name( Lines ) //@endcond public: virtual QByteArray as7BitString( bool withHeaderType = true ) const; virtual QString asUnicodeString() const; virtual void clear(); virtual bool isEmpty() const; /** Returns the number of lines, undefined if isEmpty() returns true. */ int numberOfLines() const; /** Sets the number of lines to @p lines. */ void setNumberOfLines( int lines ); protected: bool parse( const char* &scursor, const char *const send, bool isCRLF = false ); private: Q_DECLARE_PRIVATE( Lines ) }; /** Represents a "User-Agent" header. */ class KMIME_EXPORT UserAgent : public Generics::Unstructured { kmime_mk_trivial_ctor_with_name( UserAgent ) }; } //namespace Headers } //namespace KMime // undefine code generation macros again #undef kmime_mk_trivial_ctor #undef kmime_mk_dptr_ctor #undef kmime_mk_trivial_ctor_with_name #endif // __KMIME_HEADERS_H__ diff --git a/kmime/tests/headertest.cpp b/kmime/tests/headertest.cpp index 49508951a..4a15177fc 100644 --- a/kmime/tests/headertest.cpp +++ b/kmime/tests/headertest.cpp @@ -1,822 +1,758 @@ /* Copyright (c) 2006 Volker Krause 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. */ #include "headertest.h" #include #include #include using namespace KMime; using namespace KMime::Headers; using namespace KMime::Headers::Generics; // the following test cases are taken from KDE mailinglists, bug reports, RFC 2045, // RFC 2183 and RFC 2822, Appendix A QTEST_KDEMAIN( HeaderTest, NoGUI ) void HeaderTest::testIdentHeader() { // empty header Headers::Generics::Ident* h = new Headers::Generics::Ident(); QVERIFY( h->isEmpty() ); // parse single identifier h->from7BitString( QByteArray( "<1162746587.784559.5038.nullmailer@svn.kde.org>" ) ); QCOMPARE( h->identifiers().count(), 1 ); QCOMPARE( h->identifiers().first(), QByteArray( "1162746587.784559.5038.nullmailer@svn.kde.org" ) ); QCOMPARE( h->asUnicodeString(), QString("<1162746587.784559.5038.nullmailer@svn.kde.org>") ); QVERIFY( !h->isEmpty() ); // clearing a header h->clear(); QVERIFY( h->isEmpty() ); QVERIFY( h->identifiers().isEmpty() ); delete h; // parse multiple identifiers h = new Headers::Generics::Ident(); h->from7BitString( QByteArray( "<1234@local.machine.example> <3456@example.net>" ) ); QCOMPARE( h->identifiers().count(), 2 ); QList ids = h->identifiers(); QCOMPARE( ids.takeFirst(), QByteArray( "1234@local.machine.example" ) ); QCOMPARE( ids.first(), QByteArray( "3456@example.net" ) ); delete h; // parse multiple identifiers with folded headers h = new Headers::Generics::Ident(); h->from7BitString( QByteArray( "<1234@local.machine.example>\n <3456@example.net>" ) ); QCOMPARE( h->identifiers().count(), 2 ); ids = h->identifiers(); QCOMPARE( ids.takeFirst(), QByteArray( "1234@local.machine.example" ) ); QCOMPARE( ids.first(), QByteArray( "3456@example.net" ) ); // appending of new identifiers (with and without angle-brackets) h->appendIdentifier( "" ); h->appendIdentifier( "78910@example.net" ); QCOMPARE( h->identifiers().count(), 4 ); // assemble the final header QCOMPARE( h->as7BitString( false ), QByteArray("<1234@local.machine.example> <3456@example.net> <78910@example.net>") ); } void HeaderTest::testAddressListHeader() { // empty header Headers::Generics::AddressList *h = new Headers::Generics::AddressList(); QVERIFY( h->isEmpty() ); // parse single simple address h->from7BitString( "joe@where.test" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray("joe@where.test") ); QCOMPARE( h->displayNames().count(), 1 ); QCOMPARE( h->displayNames().first(), QString() ); QCOMPARE( h->prettyAddresses().count(), 1 ); QCOMPARE( h->prettyAddresses().first(), QString("joe@where.test") ); // clearing a header h->clear(); QVERIFY( h->isEmpty() ); delete h; // parsing and re-assembling a single address with display name h = new Headers::Generics::AddressList(); h->from7BitString( "Pete " ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray( "pete@silly.example" ) ); QCOMPARE( h->displayNames().first(), QString("Pete") ); QCOMPARE( h->prettyAddresses().first(), QString("Pete ") ); QCOMPARE( h->as7BitString( false ), QByteArray("Pete ") ); delete h; // parsing a single address with legacy comment style display name h = new Headers::Generics::AddressList(); h->from7BitString( "jdoe@machine.example (John Doe)" ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray( "jdoe@machine.example" ) ); QCOMPARE( h->displayNames().first(), QString("John Doe") ); QCOMPARE( h->prettyAddresses().first(), QString("John Doe ") ); delete h; // parsing and re-assembling list of diffrent addresses h = new Headers::Generics::AddressList(); h->from7BitString( "Mary Smith , jdoe@example.org, Who? " ); QCOMPARE( h->addresses().count(), 3 ); QStringList names = h->displayNames(); QCOMPARE( names.takeFirst(), QString("Mary Smith") ); QCOMPARE( names.takeFirst(), QString() ); QCOMPARE( names.takeFirst(), QString("Who?") ); QCOMPARE( h->as7BitString( false ), QByteArray("Mary Smith , jdoe@example.org, Who? ") ); delete h; // same again with some interessting quoting h = new Headers::Generics::AddressList(); h->from7BitString( "\"Joe Q. Public\" , , \"Giant; \\\"Big\\\" Box\" " ); QCOMPARE( h->addresses().count(), 3 ); names = h->displayNames(); QCOMPARE( names.takeFirst(), QString("Joe Q. Public") ); QCOMPARE( names.takeFirst(), QString() ); QCOMPARE( names.takeFirst(), QString("Giant; \"Big\" Box") ); QCOMPARE( h->as7BitString( false ), QByteArray("\"Joe Q. Public\" , boss@nil.test, \"Giant; \\\"Big\\\" Box\" ") ); delete h; // a display name with non-latin1 content h = new Headers::Generics::AddressList(); h->from7BitString( "Ingo =?iso-8859-15?q?Kl=F6cker?= " ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray( "kloecker@kde.org" ) ); QCOMPARE( h->displayNames().first(), QString::fromUtf8("Ingo Klöcker") ); QCOMPARE( h->asUnicodeString(), QString::fromUtf8("Ingo Klöcker ") ); QCOMPARE( h->as7BitString( false ), QByteArray("Ingo =?ISO-8859-1?Q?Kl=F6cker?= ") ); delete h; // again, this time legacy style h = new Headers::Generics::AddressList(); h->from7BitString( "kloecker@kde.org (Ingo =?iso-8859-15?q?Kl=F6cker?=)" ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray( "kloecker@kde.org" ) ); QCOMPARE( h->displayNames().first(), QString::fromUtf8("Ingo Klöcker") ); delete h; // parsing a empty group h = new Headers::Generics::AddressList(); h->from7BitString( "Undisclosed recipients:;" ); QCOMPARE( h->addresses().count(), 0 ); delete h; // parsing and re-assembling a address list with a group h = new Headers::Generics::AddressList(); h->from7BitString( "A Group:Chris Jones ,joe@where.test,John ;" ); QCOMPARE( h->addresses().count(), 3 ); names = h->displayNames(); QCOMPARE( names.takeFirst(), QString("Chris Jones") ); QCOMPARE( names.takeFirst(), QString() ); QCOMPARE( names.takeFirst(), QString("John") ); QCOMPARE( h->as7BitString( false ), QByteArray("Chris Jones , joe@where.test, John ") ); delete h; // modifying a header h = new Headers::Generics::AddressList(); h->from7BitString( "John " ); h->addAddress( "", QString::fromUtf8("Ingo Klöcker") ); h->addAddress( "c@a.test" ); QCOMPARE( h->addresses().count(), 3 ); QCOMPARE( h->asUnicodeString(), QString::fromUtf8("John , Ingo Klöcker , c@a.test") ); QCOMPARE( h->as7BitString( false ), QByteArray("John , Ingo =?ISO-8859-1?Q?Kl=F6cker?= , c@a.test") ); delete h; // parsing from utf-8 h = new Headers::Generics::AddressList(); h->fromUnicodeString( QString::fromUtf8("Ingo Klöcker "), "utf-8" ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray( "kloecker@kde.org" ) ); QCOMPARE( h->displayNames().first(), QString::fromUtf8("Ingo Klöcker") ); delete h; // based on bug #137033, a header broken in various ways: ';' as list separator, // unquoted '.' in display name h = new Headers::Generics::AddressList(); h->from7BitString( "Vice@censored.serverkompetenz.net,\n President@mail2.censored.net;\"Int\\\\\\\\\\\\\\\\\\\\'l\" Lotto Commission. " ); QCOMPARE( h->addresses().count(), 3 ); names = h->displayNames(); QCOMPARE( names.takeFirst(), QString() ); QCOMPARE( names.takeFirst(), QString() ); // there is an wrong ' ' after the name, but since the header is completely // broken we can be happy it parses at all... QCOMPARE( names.takeFirst(), QString("Int\\\\\\\\\\'l Lotto Commission. ") ); QList addrs = h->addresses(); QCOMPARE( addrs.takeFirst(), QByteArray("Vice@censored.serverkompetenz.net") ); QCOMPARE( addrs.takeFirst(), QByteArray("President@mail2.censored.net") ); QCOMPARE( addrs.takeFirst(), QByteArray("censored@yahoo.fr") ); delete h; // based on bug #102010, a display name containing '<' h = new Headers::Generics::AddressList( 0, QByteArray("\"|") ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray("censored@censored.dy") ); QCOMPARE( h->displayNames().first(), QString("|as7BitString( false ), QByteArray("\"|") ); // based on bug #93790 (legacy display name with nested comments) h = new Headers::Generics::AddressList( 0, QByteArray("first.name@domain.tld (first name (nickname))") ); QCOMPARE( h->displayNames().count(), 1 ); QCOMPARE( h->displayNames().first(), QString("first name (nickname)") ); QCOMPARE( h->as7BitString( false ), QByteArray("\"first name (nickname)\" ") ); delete h; // rfc 2047 encoding in quoted name (which is not allowed there) h = new Headers::Generics::AddressList(); h->from7BitString( QByteArray( "\"Ingo =?iso-8859-15?q?Kl=F6cker?=\" " ) ); QCOMPARE( h->mailboxes().count(), 1 ); QCOMPARE( h->asUnicodeString(), QString::fromUtf8( "Ingo =?iso-8859-15?q?Kl=F6cker?= " ) ); delete h; } void HeaderTest::testMailboxListHeader() { // empty header Headers::Generics::MailboxList *h = new Headers::Generics::MailboxList(); QVERIFY( h->isEmpty() ); // parse single simple address h->from7BitString( "joe_smith@where.test" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->mailboxes().count(), 1 ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray("joe_smith@where.test") ); QCOMPARE( h->displayNames().count(), 1 ); QCOMPARE( h->displayNames().first(), QString() ); QCOMPARE( h->prettyAddresses().count(), 1 ); QCOMPARE( h->prettyAddresses().first(), QString("joe_smith@where.test") ); // https://bugzilla.novell.com/show_bug.cgi?id=421057 (but apparently this was not the cause of the bug) h->from7BitString( "fr...@ce.sco (Francesco)" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->mailboxes().count(), 1 ); QCOMPARE( h->prettyAddresses().first(), QString("Francesco ") ); delete h; } void HeaderTest::testSingleMailboxHeader() { // empty header Headers::Generics::SingleMailbox *h = new Headers::Generics::SingleMailbox(); QVERIFY( h->isEmpty() ); // parse single simple address h->from7BitString( "joe_smith@where.test" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->addresses().count(), 1 ); QCOMPARE( h->addresses().first(), QByteArray("joe_smith@where.test") ); QCOMPARE( h->displayNames().count(), 1 ); QCOMPARE( h->displayNames().first(), QString() ); QCOMPARE( h->prettyAddresses().count(), 1 ); QCOMPARE( h->prettyAddresses().first(), QString("joe_smith@where.test") ); delete h; } void HeaderTest::testMailCopiesToHeader() { Headers::MailCopiesTo *h; // empty header h = new Headers::MailCopiesTo(); QVERIFY( h->isEmpty() ); QVERIFY( !h->alwaysCopy() ); QVERIFY( !h->neverCopy() ); // set to always copy to poster h->setAlwaysCopy(); QVERIFY( !h->isEmpty() ); QVERIFY( h->alwaysCopy() ); QVERIFY( !h->neverCopy() ); QCOMPARE( h->as7BitString(), QByteArray( "Mail-Copies-To: poster" ) ); // set to never copy h->setNeverCopy(); QVERIFY( !h->isEmpty() ); QVERIFY( !h->alwaysCopy() ); QVERIFY( h->neverCopy() ); QCOMPARE( h->as7BitString(), QByteArray( "Mail-Copies-To: nobody" ) ); // clear header h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse copy to poster h = new MailCopiesTo( 0, "always" ); QVERIFY( h->addresses().isEmpty() ); QVERIFY( !h->isEmpty() ); QVERIFY( h->alwaysCopy() ); delete h; // parse never copy h = new MailCopiesTo( 0, "never" ); QVERIFY( h->addresses().isEmpty() ); QVERIFY( !h->isEmpty() ); QVERIFY( h->neverCopy() ); delete h; // parse address h = new MailCopiesTo( 0, "vkrause@kde.org" ); QVERIFY( !h->addresses().isEmpty() ); QVERIFY( h->alwaysCopy() ); QVERIFY( !h->neverCopy() ); QCOMPARE( h->as7BitString(), QByteArray( "Mail-Copies-To: vkrause@kde.org" ) ); delete h; } void HeaderTest::testParametrizedHeader() { Parametrized *h; // empty header h = new Parametrized(); QVERIFY( h->isEmpty() ); // add a parameter h->setParameter( "filename", "bla.jpg" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->parameter( "filename" ), QString( "bla.jpg" ) ); QCOMPARE( h->as7BitString( false ), QByteArray( "filename=\"bla.jpg\"" ) ); // clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a parameter list h = new Parametrized( 0, "filename=genome.jpeg;\n modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"" ); QCOMPARE( h->parameter( "filename" ), QString( "genome.jpeg" ) ); QCOMPARE( h->parameter( "modification-date" ), QString( "Wed, 12 Feb 1997 16:29:51 -0500" ) ); QCOMPARE( h->as7BitString( false ), QByteArray( "filename=\"genome.jpeg\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"" ) ); delete h; // quoting of whitespaces in parameter value h = new Parametrized(); h->setParameter( "boundary", "simple boundary" ); QCOMPARE( h->as7BitString( false ), QByteArray( "boundary=\"simple boundary\"" ) ); delete h; // TODO: test RFC 2047 encoded values // TODO: test case-insensitive key-names } void HeaderTest::testContentDispositionHeader() { ContentDisposition *h; // empty header h = new ContentDisposition(); QVERIFY( h->isEmpty() ); // set some values h->setFilename( "test.jpg" ); QVERIFY( h->isEmpty() ); QVERIFY( h->as7BitString( false ).isEmpty() ); h->setDisposition( CDattachment ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString( false ), QByteArray( "attachment; filename=\"test.jpg\"" ) ); delete h; // parse parameter-less header h = new ContentDisposition( 0, "inline" ); QCOMPARE( h->disposition(), CDinline ); QVERIFY( h->filename().isEmpty() ); QCOMPARE( h->as7BitString( true ), QByteArray( "Content-Disposition: inline" ) ); delete h; // parse header with parameter h = new ContentDisposition( 0, "attachment; filename=genome.jpeg;\n modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";"); QCOMPARE( h->disposition(), CDattachment ); QCOMPARE( h->filename(), QString( "genome.jpeg" ) ); delete h; // TODO: test for case-insensitive disposition value } void HeaderTest::testContentTypeHeader() { ContentType* h; // empty header h = new ContentType(); QVERIFY( h->isEmpty() ); // Empty content-type means text/plain (RFC 2045 §5.2) QVERIFY( h->isPlainText() ); QVERIFY( h->isText() ); // set a mimetype h->setMimeType( "text/plain" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->mimeType(), QByteArray( "text/plain" ) ); QCOMPARE( h->mediaType(), QByteArray("text") ); QCOMPARE( h->subType(), QByteArray("plain") ); QVERIFY( h->isText() ); QVERIFY( h->isPlainText() ); QVERIFY( !h->isMultipart() ); QVERIFY( !h->isPartial() ); QVERIFY( h->isMediatype( "text" ) ); QVERIFY( h->isSubtype( "plain" ) ); QCOMPARE( h->as7BitString( true ), QByteArray( "Content-Type: text/plain" ) ); // add some parameters h->setId( "bla" ); h->setCharset( "us-ascii" ); QCOMPARE( h->as7BitString( false ), QByteArray( "text/plain; charset=\"us-ascii\"; id=\"bla\"" ) ); // clear header h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a complete header h = new ContentType( 0, "text/plain; charset=us-ascii (Plain text)" ); QVERIFY( h->isPlainText() ); QCOMPARE( h->charset(), QByteArray( "us-ascii" ) ); delete h; // bug #136631 (name with rfc 2231 style parameter wrapping) h = new ContentType( 0, "text/plain;\n name*0=\"PIN_Brief_box1@xx.xxx.censored_Konfigkarte.confi\";\n name*1=\"guration.txt\"" ); QVERIFY( h->isPlainText() ); QCOMPARE( h->name(), QString( "PIN_Brief_box1@xx.xxx.censored_Konfigkarte.configuration.txt" ) ); delete h; } void HeaderTest::testTokenHeader() { Token *h; // empty header h = new Token(); QVERIFY( h->isEmpty() ); // set a token h->setToken( "bla" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString( false ), QByteArray( "bla" ) ); // clear it again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a header h = new Token( 0, "value (comment)" ); QCOMPARE( h->token(), QByteArray("value") ); QCOMPARE( h->as7BitString( false ), QByteArray("value") ); delete h; } void HeaderTest::testContentTransferEncoding() { ContentTransferEncoding *h; // empty header h = new ContentTransferEncoding(); QVERIFY( h->isEmpty() ); // set an encoding h->setEncoding( CEbinary ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString( true ), QByteArray("Content-Transfer-Encoding: binary") ); // clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a header h = new ContentTransferEncoding( 0, "(comment) base64" ); QCOMPARE( h->encoding(), CEbase64 ); QCOMPARE( h->as7BitString( false ), QByteArray("base64") ); delete h; } void HeaderTest::testPhraseListHeader() { PhraseList *h; // empty header h = new PhraseList(); QVERIFY( h->isEmpty() ); delete h; // parse a simple phrase list h = new PhraseList( 0, "foo,\n bar" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->phrases().count(), 2 ); QStringList phrases = h->phrases(); QCOMPARE( phrases.takeFirst(), QString( "foo" ) ); QCOMPARE( phrases.takeFirst(), QString( "bar" ) ); QCOMPARE( h->as7BitString( false ), QByteArray("foo, bar") ); // clear header h->clear(); QVERIFY( h->isEmpty() ); delete h; // TODO: encoded/quoted phrases } void HeaderTest::testDotAtomHeader() { DotAtom *h; // empty header h = new DotAtom; QVERIFY( h->isEmpty() ); // parse a simple dot atom h->from7BitString( "1.0 (mime version)" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->asUnicodeString(), QString( "1.0" ) ); // clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // TODO: more complex atoms } void HeaderTest::testDateHeader() { Date *h; // empty header h = new Date(); QVERIFY( h->isEmpty() ); // parse a simple date h->from7BitString( "Fri, 21 Nov 1997 09:55:06 -0600" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->dateTime().date(), QDate( 1997, 11, 21 ) ); QCOMPARE( h->dateTime().time(), QTime( 9, 55, 6 ) ); QCOMPARE( h->dateTime().utcOffset(), -6 * 3600 ); QCOMPARE( h->as7BitString(), QByteArray( "Date: Fri, 21 Nov 1997 09:55:06 -0600" ) ); // clear it again h->clear(); QVERIFY( h->isEmpty() ); delete h; // white spaces and comment (from RFC 2822, Appendix A.5) h = new Date( 0, "Thu,\n 13\n Feb\n 1969\n 23:32\n -0330 (Newfoundland Time)" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->dateTime().date(), QDate( 1969, 2, 13 ) ); QCOMPARE( h->dateTime().time(), QTime( 23, 32 ) ); QCOMPARE( h->dateTime().utcOffset(), -12600 ); QCOMPARE( h->as7BitString( false ), QByteArray( "Thu, 13 Feb 1969 23:32 -0330" ) ); delete h; // obsolete date format (from RFC 2822, Appendix A.6.2) h = new Date( 0, "21 Nov 97 09:55:06 GMT" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->dateTime().date(), QDate( 1997, 11, 21 ) ); QCOMPARE( h->dateTime().time(), QTime( 9, 55, 6 ) ); QCOMPARE( h->dateTime().utcOffset(), 0 ); delete h; // obsolete whitespaces and commnets (from RFC 2822, Appendix A.6.3) h = new Date( 0, "Fri, 21 Nov 1997 09(comment): 55 : 06 -0600" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->dateTime().date(), QDate( 1997, 11, 21 ) ); QCOMPARE( h->dateTime().time(), QTime( 9, 55, 6 ) ); QCOMPARE( h->dateTime().utcOffset(), -6 * 3600 ); delete h; } void HeaderTest::testLinesHeader() { Lines *h; // empty header h = new Lines(); QVERIFY( h->isEmpty() ); QVERIFY( h->as7BitString().isEmpty() ); // set some content h->setNumberOfLines( 5 ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString(), QByteArray( "Lines: 5" ) ); // clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse header with comment h = new Lines( 0, "(this is a comment) 10 (and yet another comment)" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->numberOfLines(), 10 ); delete h; } void HeaderTest::testNewsgroupsHeader() { Newsgroups *h; // empty header h = new Newsgroups(); QVERIFY( h->isEmpty() ); QVERIFY( h->as7BitString().isEmpty() ); // set newsgroups QList groups; groups << "gmane.comp.kde.devel.core" << "gmane.comp.kde.devel.buildsystem"; h->setGroups( groups ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString(), QByteArray( "Newsgroups: gmane.comp.kde.devel.core,gmane.comp.kde.devel.buildsystem" ) ); // and clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a header h = new Newsgroups( 0, "gmane.comp.kde.devel.core,gmane.comp.kde.devel.buildsystem" ); groups = h->groups(); QCOMPARE( groups.count(), 2 ); QCOMPARE( groups.takeFirst(), QByteArray("gmane.comp.kde.devel.core") ); QCOMPARE( groups.takeFirst(), QByteArray("gmane.comp.kde.devel.buildsystem") ); delete h; // same again, this time with whitespaces and comments h = new Newsgroups(); h->from7BitString( "(comment) gmane.comp.kde.devel.core (second comment),\n gmane.comp.kde.devel.buildsystem (that all)" ); groups = h->groups(); QCOMPARE( groups.count(), 2 ); QCOMPARE( groups.takeFirst(), QByteArray("gmane.comp.kde.devel.core") ); QCOMPARE( groups.takeFirst(), QByteArray("gmane.comp.kde.devel.buildsystem") ); delete h; } void HeaderTest::testControlHeader() { Control *h; // empty header h = new Control(); QVERIFY( h->isEmpty() ); QVERIFY( h->as7BitString().isEmpty() ); // set some content h->setCancel( "" ); QVERIFY( !h->isEmpty() ); QVERIFY( h->isCancel() ); QCOMPARE( h->as7BitString(), QByteArray( "Control: cancel " ) ); // clear again h->clear(); QVERIFY( h->isEmpty() ); delete h; // parse a control header h = new Control( 0, "cancel " ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->parameter(), QByteArray("") ); QVERIFY( h->isCancel() ); QCOMPARE( h->controlType(), QByteArray("cancel") ); delete h; } void HeaderTest::testReturnPath() { ReturnPath *h; h = new ReturnPath(); QVERIFY( h->isEmpty() ); QVERIFY( h->as7BitString().isEmpty() ); h->from7BitString( "" ); QVERIFY( !h->isEmpty() ); QCOMPARE( h->as7BitString( true ), QByteArray( "Return-Path: " ) ); delete h; } void HeaderTest::noAbstractHeaders() { From* h2 = new From(); delete h2; Sender* h3 = new Sender(); delete h3; To* h4 = new To(); delete h4; Cc* h5 = new Cc(); delete h5; Bcc* h6 = new Bcc(); delete h6; ReplyTo* h7 = new ReplyTo(); delete h7; Keywords* h8 = new Keywords(); delete h8; MIMEVersion* h9 = new MIMEVersion(); delete h9; MessageID* h10 = new MessageID(); delete h10; ContentID* h11 = new ContentID(); delete h11; Supersedes* h12 = new Supersedes(); delete h12; InReplyTo* h13 = new InReplyTo(); delete h13; References* h14 = new References(); delete h14; Generic* h15 = new Generic(); delete h15; Subject* h16 = new Subject(); delete h16; Organization* h17 = new Organization(); delete h17; ContentDescription* h18 = new ContentDescription(); delete h18; FollowUpTo* h22 = new FollowUpTo(); delete h22; UserAgent* h24 = new UserAgent(); delete h24; } -void HeaderTest::testClone() -{ - // Clone a simple header. - { - Subject *h = new Subject; - h->from7BitString( "our fortress is burning" ); - Subject *c = dynamic_cast( h->clone() ); - QVERIFY( c ); - h->from7BitString( "against the grain of the shattered sky" ); - QCOMPARE( c->as7BitString( false ), QByteArray( "our fortress is burning" ) ); - delete h; - delete c; - } - - // Clone a more complex header. - { - ContentType *h = new ContentType; - h->setMimeType( "some/x-type" ); - h->setCharset( "ancient-runes" ); - ContentType *c = dynamic_cast( h->clone() ); - QVERIFY( c ); - h->clear(); - QCOMPARE( c->mimeType(), QByteArray( "some/x-type" ) ); - QCOMPARE( c->charset(), QByteArray( "ancient-runes" ) ); - delete h; - delete c; - } - - // Clone an even more complex header. - { - const QString address1( "Me " ); - const QString address2( "You " ); - To *h = new To; - Types::Mailbox box; - box.fromUnicodeString( address1 ); - h->addAddress( box ); - box.fromUnicodeString( address2 ); - h->addAddress( box ); - To *c = dynamic_cast( h->clone() ); - QVERIFY( c ); - h->clear(); - QCOMPARE( c->prettyAddresses().count(), 2 ); - QCOMPARE( c->prettyAddresses().at( 0 ), address1 ); - QCOMPARE( c->prettyAddresses().at( 1 ), address2 ); - delete h; - delete c; - } - - // Special test for Generic since it has a special clone() implementation. - { - Generic *h = new Generic; - h->setType( "type" ); - h->from7BitString( "beforecataracts" ); - Generic *c = dynamic_cast( h->clone() ); - QVERIFY( c ); - h->setType( "newtype" ); - h->from7BitString( "aftercataracts" ); - QCOMPARE( c->type(), "type" ); - QCOMPARE( c->as7BitString( false ), QByteArray( "beforecataracts" ) ); - delete h; - delete c; - } -} - void HeaderTest::testInvalidButOkQEncoding() { // A stray '?' should not confuse the parser Subject subject; subject.from7BitString( "=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC?" "?=" ); QCOMPARE( subject.as7BitString( false ), QByteArray( "Why? Why do some clients violate the RFC?" ) ); } void HeaderTest::testInvalidQEncoding_data() { QTest::addColumn("encodedWord"); // All examples below should not be treated as invalid encoded strings, since the '?=' is missing QTest::newRow("") << QString( "=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC??" ); QTest::newRow("") << QString( "=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC?" ); QTest::newRow("") << QString( "=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC" ); } void HeaderTest::testInvalidQEncoding() { using namespace HeaderParsing; QFETCH( QString,encodedWord ); const char *data = encodedWord.toAscii().data(); const char *start = data + 1; const char *end = data + strlen( data ); QString result; QByteArray language; QByteArray usedCS; QVERIFY( !parseEncodedWord( start, end, result, language, usedCS ) ); } #include "headertest.moc" diff --git a/kmime/tests/headertest.h b/kmime/tests/headertest.h index 250102be3..760fd6932 100644 --- a/kmime/tests/headertest.h +++ b/kmime/tests/headertest.h @@ -1,58 +1,56 @@ /* Copyright (c) 2006 Volker Krause 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. */ #ifndef KMIME_HEADERTEST_H #define KMIME_HEADERTEST_H #include class HeaderTest : public QObject { Q_OBJECT private Q_SLOTS: void testIdentHeader(); void testAddressListHeader(); void testMailboxListHeader(); void testSingleMailboxHeader(); void testMailCopiesToHeader(); void testParametrizedHeader(); void testContentDispositionHeader(); void testContentTypeHeader(); void testTokenHeader(); void testContentTransferEncoding(); void testPhraseListHeader(); void testDotAtomHeader(); void testDateHeader(); void testLinesHeader(); void testNewsgroupsHeader(); void testControlHeader(); void testReturnPath(); void testInvalidButOkQEncoding(); void testInvalidQEncoding(); void testInvalidQEncoding_data(); // makes sure we don't accidently have an abstract header class that's not // meant to be abstract void noAbstractHeaders(); - - void testClone(); }; #endif