diff --git a/kmime/kmime_headers.cpp b/kmime/kmime_headers.cpp index 81fc8174b..35c1d967a 100644 --- a/kmime/kmime_headers.cpp +++ b/kmime/kmime_headers.cpp @@ -1,2152 +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 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 \ */ #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_p.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 ); \ } \ \ 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 ); \ } \ \ 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(); } 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; + d->neverCopy = 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