• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KMIME Library

kmime_headers.cpp

Go to the documentation of this file.
00001 /*  -*- c++ -*-
00002     kmime_headers.cpp
00003 
00004     KMime, the KDE Internet mail/usenet news message library.
00005     Copyright (c) 2001-2002 the KMime authors.
00006     See file AUTHORS for details
00007     Copyright (c) 2006 Volker Krause <vkrause@kde.org>
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022     Boston, MA 02110-1301, USA.
00023 */
00040 #include "kmime_headers.h"
00041 #include "kmime_headers_p.h"
00042 
00043 #include "kmime_util.h"
00044 #include "kmime_content.h"
00045 #include "kmime_codecs.h"
00046 #include "kmime_header_parsing.h"
00047 #include "kmime_headerfactory_p.h"
00048 #include "kmime_warning.h"
00049 
00050 #include <QtCore/QTextCodec>
00051 #include <QtCore/QString>
00052 #include <QtCore/QStringList>
00053 
00054 #include <kglobal.h>
00055 #include <kcharsets.h>
00056 
00057 #include <assert.h>
00058 #include <ctype.h>
00059 
00060 template <typename T>
00061 bool registerHeaderHelper()
00062 {
00063   const T dummy;
00064   if( QByteArray( dummy.type() ).isEmpty() ) {
00065     // This is a generic header.
00066     return false;
00067   }
00068   return KMime::HeaderFactory::self()->registerHeader<T>();
00069 }
00070 
00071 // macro to register a header with HeaderFactory
00072 #define kmime_register_header( subclass )                             \
00073 namespace { const bool dummyForRegistering##subclass = registerHeaderHelper<subclass>(); }
00074 
00075 // macro to generate a default constructor implementation
00076 #define kmime_mk_trivial_ctor( subclass, baseclass )                  \
00077 subclass::subclass( Content *parent ) : baseclass( parent )           \
00078 {                                                                     \
00079   clear();                                                            \
00080 }                                                                     \
00081                                                                       \
00082 subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( parent ) \
00083 {                                                                     \
00084   from7BitString( s );                                                \
00085 }                                                                     \
00086                                                                       \
00087 subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
00088   baseclass( parent )                                                 \
00089 {                                                                     \
00090   fromUnicodeString( s, charset );                                    \
00091 }                                                                     \
00092                                                                       \
00093 subclass::~subclass() {}                                              \
00094                                                                       \
00095 kmime_register_header( subclass )
00096 // end kmime_mk_trivial_ctor
00097 
00098 
00099 #define kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
00100 subclass::subclass( Content *parent ) : baseclass( new subclass##Private, parent ) \
00101 {                                                                     \
00102   clear();                                                            \
00103 }                                                                     \
00104                                                                       \
00105 subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( new subclass##Private, parent ) \
00106 {                                                                     \
00107   from7BitString( s );                                                \
00108 }                                                                     \
00109                                                                       \
00110 subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
00111   baseclass( new subclass##Private, parent )                          \
00112 {                                                                     \
00113   fromUnicodeString( s, charset );                                    \
00114 }                                                                     \
00115                                                                       \
00116 subclass::~subclass() {}                                              \
00117                                                                       \
00118 kmime_register_header( subclass )
00119 // end kmime_mk_trivial_ctor_with_dptr
00120 
00121 
00122 #define kmime_mk_trivial_ctor_with_name( subclass, baseclass, name )  \
00123 kmime_mk_trivial_ctor( subclass, baseclass )                          \
00124                                                                       \
00125 const char *subclass::type() const                                    \
00126 {                                                                     \
00127   return #name;                                                       \
00128 }
00129 
00130 #define kmime_mk_trivial_ctor_with_name_and_dptr( subclass, baseclass, name ) \
00131 kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
00132 const char *subclass::type() const { return #name; }
00133 
00134 #define kmime_mk_dptr_ctor( subclass, baseclass ) \
00135 subclass::subclass( subclass##Private *d, KMime::Content *parent ) : baseclass( d, parent ) {}
00136 
00137 using namespace KMime;
00138 using namespace KMime::Headers;
00139 using namespace KMime::Types;
00140 using namespace KMime::HeaderParsing;
00141 
00142 namespace KMime {
00143 namespace Headers {
00144 //-----<Base>----------------------------------
00145 Base::Base( KMime::Content *parent ) :
00146     d_ptr( new BasePrivate )
00147 {
00148   Q_D(Base);
00149   d->parent = parent;
00150 }
00151 
00152 Base::Base( BasePrivate *dd, KMime::Content *parent ) :
00153     d_ptr( dd )
00154 {
00155   Q_D(Base);
00156   d->parent = parent;
00157 }
00158 
00159 Base::~Base()
00160 {
00161   delete d_ptr;
00162   d_ptr = 0;
00163 }
00164 
00165 KMime::Content *Base::parent() const
00166 {
00167   return d_ptr->parent;
00168 }
00169 
00170 void Base::setParent( KMime::Content *parent )
00171 {
00172   d_ptr->parent = parent;
00173 }
00174 
00175 QByteArray Base::rfc2047Charset() const
00176 {
00177   if ( d_ptr->encCS.isEmpty() || forceDefaultCharset() ) {
00178     return defaultCharset();
00179   } else {
00180     return d_ptr->encCS;
00181   }
00182 }
00183 
00184 void Base::setRFC2047Charset( const QByteArray &cs )
00185 {
00186   d_ptr->encCS = cachedCharset( cs );
00187 }
00188 
00189 bool Base::forceDefaultCharset() const
00190 {
00191   return ( parent() != 0 ? parent()->forceDefaultCharset() : false );
00192 }
00193 
00194 QByteArray Base::defaultCharset() const
00195 {
00196   return ( parent() != 0 ? parent()->defaultCharset() : Latin1 );
00197 }
00198 
00199 const char *Base::type() const
00200 {
00201   return "";
00202 }
00203 
00204 bool Base::is( const char *t ) const
00205 {
00206   return strcasecmp( t, type() ) == 0;
00207 }
00208 
00209 bool Base::isMimeHeader() const
00210 {
00211   return strncasecmp( type(), "Content-", 8 ) == 0;
00212 }
00213 
00214 bool Base::isXHeader() const
00215 {
00216   return strncmp( type(), "X-", 2 ) == 0;
00217 }
00218 
00219 QByteArray Base::typeIntro() const
00220 {
00221   return QByteArray( type() ) + ": ";
00222 }
00223 
00224 //-----</Base>---------------------------------
00225 
00226 namespace Generics {
00227 
00228 //-----<Unstructured>-------------------------
00229 
00230 //@cond PRIVATE
00231 kmime_mk_dptr_ctor( Unstructured, Base )
00232 //@endcond
00233 
00234 Unstructured::Unstructured( Content *p ) : Base( new UnstructuredPrivate, p )
00235 {
00236 }
00237 
00238 Unstructured::Unstructured( Content *p, const QByteArray &s ) : Base( new UnstructuredPrivate, p )
00239 {
00240   from7BitString( s );
00241 }
00242 
00243 Unstructured::Unstructured( Content *p, const QString &s, const QByteArray &cs ) : Base( new UnstructuredPrivate, p )
00244 {
00245   fromUnicodeString( s, cs );
00246 }
00247 
00248 Unstructured::~Unstructured()
00249 {
00250 }
00251 
00252 void Unstructured::from7BitString( const QByteArray &s )
00253 {
00254   Q_D(Unstructured);
00255   d->decoded = decodeRFC2047String( s, d->encCS, defaultCharset(), forceDefaultCharset() );
00256 }
00257 
00258 QByteArray Unstructured::as7BitString( bool withHeaderType ) const
00259 {
00260   const Q_D(Unstructured);
00261   QByteArray result;
00262   if ( withHeaderType ) {
00263     result = typeIntro();
00264   }
00265   result += encodeRFC2047String( d->decoded, d->encCS ) ;
00266 
00267   return result;
00268 }
00269 
00270 void Unstructured::fromUnicodeString( const QString &s, const QByteArray &b )
00271 {
00272   Q_D(Unstructured);
00273   d->decoded = s;
00274   d->encCS = cachedCharset( b );
00275 }
00276 
00277 QString Unstructured::asUnicodeString() const
00278 {
00279   return d_func()->decoded;
00280 }
00281 
00282 void Unstructured::clear()
00283 {
00284   Q_D(Unstructured);
00285   d->decoded.truncate( 0 );
00286 }
00287 
00288 bool Unstructured::isEmpty() const
00289 {
00290   return d_func()->decoded.isEmpty();
00291 }
00292 
00293 //-----</Unstructured>-------------------------
00294 
00295 //-----<Structured>-------------------------
00296 
00297 Structured::Structured( Content *p ) : Base( new StructuredPrivate, p )
00298 {
00299 }
00300 
00301 Structured::Structured( Content *p, const QByteArray &s ) : Base( new StructuredPrivate, p )
00302 {
00303   from7BitString( s );
00304 }
00305 
00306 Structured::Structured( Content *p, const QString &s, const QByteArray &cs ) : Base( new StructuredPrivate, p )
00307 {
00308   fromUnicodeString( s, cs );
00309 }
00310 
00311 kmime_mk_dptr_ctor( Structured, Base )
00312 
00313 Structured::~Structured()
00314 {
00315 }
00316 
00317 void Structured::from7BitString( const QByteArray &s )
00318 {
00319   Q_D(Structured);
00320   if ( d->encCS.isEmpty() ) {
00321     d->encCS = defaultCharset();
00322   }
00323   const char *cursor = s.constData();
00324   parse( cursor, cursor + s.length() );
00325 }
00326 
00327 QString Structured::asUnicodeString() const
00328 {
00329   return QString::fromLatin1( as7BitString( false ) );
00330 }
00331 
00332 void Structured::fromUnicodeString( const QString &s, const QByteArray &b )
00333 {
00334   Q_D(Structured);
00335   d->encCS = cachedCharset( b );
00336   from7BitString( s.toLatin1() );
00337 }
00338 
00339 //-----</Structured>-------------------------
00340 
00341 //-----<Address>-------------------------
00342 
00343 Address::Address( Content *p ) : Structured( new AddressPrivate, p )
00344 {
00345 }
00346 
00347 Address::Address( Content *p, const QByteArray &s ) : Structured( new AddressPrivate, p )
00348 {
00349   from7BitString( s );
00350 }
00351 
00352 Address::Address( Content *p, const QString &s, const QByteArray &cs ) : Structured( new AddressPrivate, p )
00353 {
00354   fromUnicodeString( s, cs );
00355 }
00356 
00357 kmime_mk_dptr_ctor( Address, Structured )
00358 
00359 Address:: ~Address()
00360 {
00361 }
00362 
00363 // helper method used in AddressList and MailboxList
00364 static bool stringToMailbox( const QByteArray &address,
00365                              const QString &displayName, Types::Mailbox &mbox )
00366 {
00367   Types::AddrSpec addrSpec;
00368   mbox.setName( displayName );
00369   const char *cursor = address.constData();
00370   if ( !parseAngleAddr( cursor, cursor + address.length(), addrSpec ) ) {
00371     if ( !parseAddrSpec( cursor, cursor + address.length(), addrSpec ) ) {
00372       kWarning() << "Invalid address";
00373       return false;
00374     }
00375   }
00376   mbox.setAddress( addrSpec );
00377   return true;
00378 }
00379 
00380 //-----</Address>-------------------------
00381 
00382 //-----<MailboxList>-------------------------
00383 
00384 kmime_mk_trivial_ctor_with_dptr( MailboxList, Address )
00385 kmime_mk_dptr_ctor( MailboxList, Address )
00386 
00387 QByteArray MailboxList::as7BitString( bool withHeaderType ) const
00388 {
00389   const Q_D(MailboxList);
00390   if ( isEmpty() ) {
00391     return QByteArray();
00392   }
00393 
00394   QByteArray rv;
00395   if ( withHeaderType ) {
00396     rv = typeIntro();
00397   }
00398   foreach ( Types::Mailbox mbox, d->mailboxList ) {
00399     rv += mbox.as7BitString( d->encCS );
00400     rv += ", ";
00401   }
00402   rv.resize( rv.length() - 2 );
00403   return rv;
00404 }
00405 
00406 void MailboxList::fromUnicodeString( const QString &s, const QByteArray &b )
00407 {
00408   Q_D(MailboxList);
00409   d->encCS = cachedCharset( b );
00410   from7BitString( encodeRFC2047String( s, b, false ) );
00411 }
00412 
00413 QString MailboxList::asUnicodeString() const
00414 {
00415   return prettyAddresses().join( QLatin1String( ", " ) );
00416 }
00417 
00418 void MailboxList::clear()
00419 {
00420   Q_D(MailboxList);
00421   d->mailboxList.clear();
00422 }
00423 
00424 bool MailboxList::isEmpty() const
00425 {
00426   return d_func()->mailboxList.isEmpty();
00427 }
00428 
00429 void MailboxList::addAddress( const Types::Mailbox &mbox )
00430 {
00431   Q_D(MailboxList);
00432   d->mailboxList.append( mbox );
00433 }
00434 
00435 void MailboxList::addAddress( const QByteArray &address,
00436                               const QString &displayName )
00437 {
00438   Q_D(MailboxList);
00439   Types::Mailbox mbox;
00440   if ( stringToMailbox( address, displayName, mbox ) ) {
00441     d->mailboxList.append( mbox );
00442   }
00443 }
00444 
00445 QList< QByteArray > MailboxList::addresses() const
00446 {
00447   QList<QByteArray> rv;
00448   foreach ( Types::Mailbox mbox, d_func()->mailboxList ) {
00449     rv.append( mbox.address() );
00450   }
00451   return rv;
00452 }
00453 
00454 QStringList MailboxList::displayNames() const
00455 {
00456   QStringList rv;
00457   foreach ( Types::Mailbox mbox, d_func()->mailboxList ) {
00458     rv.append( mbox.name() );
00459   }
00460   return rv;
00461 }
00462 
00463 QStringList MailboxList::prettyAddresses() const
00464 {
00465   QStringList rv;
00466   foreach ( Types::Mailbox mbox, d_func()->mailboxList ) {
00467     rv.append( mbox.prettyAddress() );
00468   }
00469   return rv;
00470 }
00471 
00472 Types::Mailbox::List MailboxList::mailboxes() const
00473 {
00474   return d_func()->mailboxList;
00475 }
00476 
00477 bool MailboxList::parse( const char* &scursor, const char *const send,
00478                          bool isCRLF )
00479 {
00480   Q_D(MailboxList);
00481   // examples:
00482   // from := "From:" mailbox-list CRLF
00483   // sender := "Sender:" mailbox CRLF
00484 
00485   // parse an address-list:
00486   QList<Types::Address> maybeAddressList;
00487   if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) {
00488     return false;
00489   }
00490 
00491   d->mailboxList.clear();
00492 
00493   // extract the mailboxes and complain if there are groups:
00494   QList<Types::Address>::Iterator it;
00495   for ( it = maybeAddressList.begin(); it != maybeAddressList.end() ; ++it ) {
00496     if ( !(*it).displayName.isEmpty() ) {
00497       KMIME_WARN << "mailbox groups in header disallowing them! Name: \""
00498                  << (*it).displayName << "\"" << endl;
00499     }
00500     d->mailboxList += (*it).mailboxList;
00501   }
00502   return true;
00503 }
00504 
00505 //-----</MailboxList>-------------------------
00506 
00507 //-----<SingleMailbox>-------------------------
00508 
00509 //@cond PRIVATE
00510 kmime_mk_trivial_ctor_with_dptr( SingleMailbox, MailboxList )
00511 //@endcond
00512 
00513 bool SingleMailbox::parse( const char* &scursor, const char *const send,
00514                              bool isCRLF )
00515 {
00516   Q_D(MailboxList);
00517   if ( !MailboxList::parse( scursor, send, isCRLF ) ) {
00518     return false;
00519   }
00520 
00521   if ( d->mailboxList.count() > 1 ) {
00522     KMIME_WARN << "multiple mailboxes in header allowing only a single one!"
00523                << endl;
00524   }
00525   return true;
00526 }
00527 
00528 //-----</SingleMailbox>-------------------------
00529 
00530 //-----<AddressList>-------------------------
00531 
00532 //@cond PRIVATE
00533 kmime_mk_trivial_ctor_with_dptr( AddressList, Address )
00534 kmime_mk_dptr_ctor( AddressList, Address )
00535 //@endcond
00536 
00537 QByteArray AddressList::as7BitString( bool withHeaderType ) const
00538 {
00539   const Q_D(AddressList);
00540   if ( d->addressList.isEmpty() ) {
00541     return QByteArray();
00542   }
00543 
00544   QByteArray rv;
00545   if ( withHeaderType ) {
00546     rv = typeIntro();
00547   }
00548   foreach ( Types::Address addr, d->addressList ) {
00549     foreach ( Types::Mailbox mbox, addr.mailboxList ) {
00550       rv += mbox.as7BitString( d->encCS );
00551       rv += ", ";
00552     }
00553   }
00554   rv.resize( rv.length() - 2 );
00555   return rv;
00556 }
00557 
00558 void AddressList::fromUnicodeString( const QString &s, const QByteArray &b )
00559 {
00560   Q_D(AddressList);
00561   d->encCS = cachedCharset( b );
00562   from7BitString( encodeRFC2047String( s, b, false ) );
00563 }
00564 
00565 QString AddressList::asUnicodeString() const
00566 {
00567   return prettyAddresses().join( QLatin1String( ", " ) );
00568 }
00569 
00570 void AddressList::clear()
00571 {
00572   Q_D(AddressList);
00573   d->addressList.clear();
00574 }
00575 
00576 bool AddressList::isEmpty() const
00577 {
00578   return d_func()->addressList.isEmpty();
00579 }
00580 
00581 void AddressList::addAddress( const Types::Mailbox &mbox )
00582 {
00583   Q_D(AddressList);
00584   Types::Address addr;
00585   addr.mailboxList.append( mbox );
00586   d->addressList.append( addr );
00587 }
00588 
00589 void AddressList::addAddress( const QByteArray &address,
00590                               const QString &displayName )
00591 {
00592   Q_D(AddressList);
00593   Types::Address addr;
00594   Types::Mailbox mbox;
00595   if ( stringToMailbox( address, displayName, mbox ) ) {
00596     addr.mailboxList.append( mbox );
00597     d->addressList.append( addr );
00598   }
00599 }
00600 
00601 QList< QByteArray > AddressList::addresses() const
00602 {
00603   QList<QByteArray> rv;
00604   foreach ( Types::Address addr, d_func()->addressList ) {
00605     foreach ( Types::Mailbox mbox, addr.mailboxList ) {
00606       rv.append( mbox.address() );
00607     }
00608   }
00609   return rv;
00610 }
00611 
00612 QStringList AddressList::displayNames() const
00613 {
00614   QStringList rv;
00615   foreach ( Types::Address addr, d_func()->addressList ) {
00616     foreach ( Types::Mailbox mbox, addr.mailboxList ) {
00617       rv.append( mbox.name() );
00618     }
00619   }
00620   return rv;
00621 }
00622 
00623 QStringList AddressList::prettyAddresses() const
00624 {
00625   QStringList rv;
00626   foreach ( Types::Address addr, d_func()->addressList ) {
00627     foreach ( Types::Mailbox mbox, addr.mailboxList ) {
00628       rv.append( mbox.prettyAddress() );
00629     }
00630   }
00631   return rv;
00632 }
00633 
00634 Types::Mailbox::List AddressList::mailboxes() const
00635 {
00636   Types::Mailbox::List rv;
00637   foreach ( Types::Address addr, d_func()->addressList ) {
00638     foreach ( Types::Mailbox mbox, addr.mailboxList ) {
00639       rv.append( mbox );
00640     }
00641   }
00642   return rv;
00643 }
00644 
00645 bool AddressList::parse( const char* &scursor, const char *const send,
00646                          bool isCRLF )
00647 {
00648   Q_D(AddressList);
00649   QList<Types::Address> maybeAddressList;
00650   if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) {
00651     return false;
00652   }
00653 
00654   d->addressList = maybeAddressList;
00655   return true;
00656 }
00657 
00658 //-----</AddressList>-------------------------
00659 
00660 //-----<Token>-------------------------
00661 
00662 //@cond PRIVATE
00663 kmime_mk_trivial_ctor_with_dptr( Token, Structured )
00664 kmime_mk_dptr_ctor( Token, Structured )
00665 //@endcond
00666 
00667 QByteArray Token::as7BitString( bool withHeaderType ) const
00668 {
00669   if ( isEmpty() ) {
00670     return QByteArray();
00671   }
00672   if ( withHeaderType ) {
00673     return typeIntro() + d_func()->token;
00674   }
00675   return d_func()->token;
00676 }
00677 
00678 void Token::clear()
00679 {
00680   Q_D(Token);
00681   d->token.clear();
00682 }
00683 
00684 bool Token::isEmpty() const
00685 {
00686   return d_func()->token.isEmpty();
00687 }
00688 
00689 QByteArray Token::token() const
00690 {
00691   return d_func()->token;
00692 }
00693 
00694 void Token::setToken( const QByteArray &t )
00695 {
00696   Q_D(Token);
00697   d->token = t;
00698 }
00699 
00700 bool Token::parse( const char* &scursor, const char *const send, bool isCRLF )
00701 {
00702   Q_D(Token);
00703   clear();
00704   eatCFWS( scursor, send, isCRLF );
00705   // must not be empty:
00706   if ( scursor == send ) {
00707     return false;
00708   }
00709 
00710   QPair<const char*,int> maybeToken;
00711   if ( !parseToken( scursor, send, maybeToken, false /* no 8bit chars */ ) ) {
00712     return false;
00713   }
00714   d->token = QByteArray( maybeToken.first, maybeToken.second );
00715 
00716   // complain if trailing garbage is found:
00717   eatCFWS( scursor, send, isCRLF );
00718   if ( scursor != send ) {
00719     KMIME_WARN << "trailing garbage after token in header allowing "
00720       "only a single token!" << endl;
00721   }
00722   return true;
00723 }
00724 
00725 //-----</Token>-------------------------
00726 
00727 //-----<PhraseList>-------------------------
00728 
00729 //@cond PRIVATE
00730 kmime_mk_trivial_ctor_with_dptr( PhraseList, Structured )
00731 //@endcond
00732 
00733 QByteArray PhraseList::as7BitString( bool withHeaderType ) const
00734 {
00735   const Q_D(PhraseList);
00736   if ( isEmpty() ) {
00737     return QByteArray();
00738   }
00739 
00740   QByteArray rv;
00741   if ( withHeaderType ) {
00742     rv = typeIntro();
00743   }
00744 
00745   for ( int i = 0; i < d->phraseList.count(); ++i ) {
00746     // FIXME: only encode when needed, quote when needed, etc.
00747     rv += encodeRFC2047String( d->phraseList[i], d->encCS, false, false );
00748     if ( i != d->phraseList.count() - 1 ) {
00749       rv += ", ";
00750     }
00751   }
00752 
00753   return rv;
00754 }
00755 
00756 QString PhraseList::asUnicodeString() const
00757 {
00758   return d_func()->phraseList.join( QLatin1String( ", " ) );
00759 }
00760 
00761 void PhraseList::clear()
00762 {
00763   Q_D(PhraseList);
00764   d->phraseList.clear();
00765 }
00766 
00767 bool PhraseList::isEmpty() const
00768 {
00769   return d_func()->phraseList.isEmpty();
00770 }
00771 
00772 QStringList PhraseList::phrases() const
00773 {
00774   return d_func()->phraseList;
00775 }
00776 
00777 bool PhraseList::parse( const char* &scursor, const char *const send,
00778                          bool isCRLF )
00779 {
00780   Q_D(PhraseList);
00781   d->phraseList.clear();
00782 
00783   while ( scursor != send ) {
00784     eatCFWS( scursor, send, isCRLF );
00785     // empty entry ending the list: OK.
00786     if ( scursor == send ) {
00787       return true;
00788     }
00789     // empty entry: ignore.
00790     if ( *scursor == ',' ) {
00791       scursor++;
00792       continue;
00793     }
00794 
00795     QString maybePhrase;
00796     if ( !parsePhrase( scursor, send, maybePhrase, isCRLF ) ) {
00797       return false;
00798     }
00799     d->phraseList.append( maybePhrase );
00800 
00801     eatCFWS( scursor, send, isCRLF );
00802     // non-empty entry ending the list: OK.
00803     if ( scursor == send ) {
00804       return true;
00805     }
00806     // comma separating the phrases: eat.
00807     if ( *scursor == ',' ) {
00808       scursor++;
00809     }
00810   }
00811   return true;
00812 }
00813 
00814 //-----</PhraseList>-------------------------
00815 
00816 //-----<DotAtom>-------------------------
00817 
00818 //@cond PRIVATE
00819 kmime_mk_trivial_ctor_with_dptr( DotAtom, Structured )
00820 //@endcond
00821 
00822 QByteArray DotAtom::as7BitString( bool withHeaderType ) const
00823 {
00824   if ( isEmpty() ) {
00825     return QByteArray();
00826   }
00827 
00828   QByteArray rv;
00829   if ( withHeaderType ) {
00830     rv += typeIntro();
00831   }
00832 
00833   rv += d_func()->dotAtom.toLatin1(); // FIXME: encoding?
00834   return rv;
00835 }
00836 
00837 QString DotAtom::asUnicodeString() const
00838 {
00839   return d_func()->dotAtom;
00840 }
00841 
00842 void DotAtom::clear()
00843 {
00844   Q_D(DotAtom);
00845   d->dotAtom.clear();
00846 }
00847 
00848 bool DotAtom::isEmpty() const
00849 {
00850   return d_func()->dotAtom.isEmpty();
00851 }
00852 
00853 bool DotAtom::parse( const char* &scursor, const char *const send,
00854                       bool isCRLF )
00855 {
00856   Q_D(DotAtom);
00857   QString maybeDotAtom;
00858   if ( !parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) {
00859     return false;
00860   }
00861 
00862   d->dotAtom = maybeDotAtom;
00863 
00864   eatCFWS( scursor, send, isCRLF );
00865   if ( scursor != send ) {
00866     KMIME_WARN << "trailing garbage after dot-atom in header allowing "
00867       "only a single dot-atom!" << endl;
00868   }
00869   return true;
00870 }
00871 
00872 //-----</DotAtom>-------------------------
00873 
00874 //-----<Parametrized>-------------------------
00875 
00876 //@cond PRIVATE
00877 kmime_mk_trivial_ctor_with_dptr( Parametrized, Structured )
00878 kmime_mk_dptr_ctor( Parametrized, Structured )
00879 //@endcond
00880 
00881 QByteArray Parametrized::as7BitString( bool withHeaderType ) const
00882 {
00883   const Q_D(Parametrized);
00884   if ( isEmpty() ) {
00885     return QByteArray();
00886   }
00887 
00888   QByteArray rv;
00889   if ( withHeaderType ) {
00890     rv += typeIntro();
00891   }
00892 
00893   bool first = true;
00894   for ( QMap<QString,QString>::ConstIterator it = d->parameterHash.constBegin();
00895         it != d->parameterHash.constEnd(); ++it )
00896   {
00897     if ( !first ) {
00898       rv += "; ";
00899     } else {
00900       first = false;
00901     }
00902     rv += it.key().toLatin1() + '=';
00903     if ( isUsAscii( it.value() ) ) {
00904       QByteArray tmp = it.value().toLatin1();
00905       addQuotes( tmp, true ); // force quoting, eg. for whitespaces in parameter value
00906       rv += tmp;
00907     } else {
00908       // FIXME: encoded strings are not allowed inside quotes, OTOH we need to quote whitespaces...
00909       rv += "\"" + encodeRFC2047String( it.value(), d->encCS ) + "\"";
00910     }
00911   }
00912 
00913   return rv;
00914 }
00915 
00916 QString Parametrized::parameter( const QString &key ) const
00917 {
00918   return d_func()->parameterHash.value( key );
00919 }
00920 
00921 void Parametrized::setParameter( const QString &key, const QString &value )
00922 {
00923   Q_D(Parametrized);
00924   d->parameterHash.insert( key, value );
00925 }
00926 
00927 bool Parametrized::isEmpty() const
00928 {
00929   return d_func()->parameterHash.isEmpty();
00930 }
00931 
00932 void Parametrized::clear()
00933 {
00934   Q_D(Parametrized);
00935   d->parameterHash.clear();
00936 }
00937 
00938 bool Parametrized::parse( const char *& scursor, const char * const send,
00939                           bool isCRLF )
00940 {
00941   Q_D(Parametrized);
00942   d->parameterHash.clear();
00943   if ( !parseParameterList( scursor, send, d->parameterHash, isCRLF ) ) {
00944     return false;
00945   }
00946   return true;
00947 }
00948 
00949 //-----</Parametrized>-------------------------
00950 
00951 //-----<Ident>-------------------------
00952 
00953 //@cond PRIVATE
00954 kmime_mk_trivial_ctor_with_dptr( Ident, Address )
00955 kmime_mk_dptr_ctor( Ident, Address )
00956 //@endcond
00957 
00958 QByteArray Ident::as7BitString( bool withHeaderType ) const
00959 {
00960   const Q_D(Ident);
00961   if ( d->msgIdList.isEmpty() ) {
00962     return QByteArray();
00963   }
00964 
00965   QByteArray rv;
00966   if ( withHeaderType ) {
00967     rv = typeIntro();
00968   }
00969   foreach ( Types::AddrSpec addr, d->msgIdList ) {
00970     rv += '<';
00971     rv += addr.asString().toLatin1(); // FIXME: change parsing to use QByteArrays
00972     rv += "> ";
00973   }
00974   rv.resize( rv.length() - 1 );
00975   return rv;
00976 }
00977 
00978 void Ident::clear()
00979 {
00980   Q_D(Ident);
00981   d->msgIdList.clear();
00982 }
00983 
00984 bool Ident::isEmpty() const
00985 {
00986   return d_func()->msgIdList.isEmpty();
00987 }
00988 
00989 bool Ident::parse( const char* &scursor, const char * const send, bool isCRLF )
00990 {
00991   Q_D(Ident);
00992   // msg-id   := "<" id-left "@" id-right ">"
00993   // id-left  := dot-atom-text / no-fold-quote / local-part
00994   // id-right := dot-atom-text / no-fold-literal / domain
00995   //
00996   // equivalent to:
00997   // msg-id   := angle-addr
00998 
00999   d->msgIdList.clear();
01000 
01001   while ( scursor != send ) {
01002     eatCFWS( scursor, send, isCRLF );
01003     // empty entry ending the list: OK.
01004     if ( scursor == send ) {
01005       return true;
01006     }
01007     // empty entry: ignore.
01008     if ( *scursor == ',' ) {
01009       scursor++;
01010       continue;
01011     }
01012 
01013     AddrSpec maybeMsgId;
01014     if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) {
01015       return false;
01016     }
01017     d->msgIdList.append( maybeMsgId );
01018 
01019     eatCFWS( scursor, send, isCRLF );
01020     // header end ending the list: OK.
01021     if ( scursor == send ) {
01022       return true;
01023     }
01024     // regular item separator: eat it.
01025     if ( *scursor == ',' ) {
01026       scursor++;
01027     }
01028   }
01029   return true;
01030 }
01031 
01032 QList<QByteArray> Ident::identifiers() const
01033 {
01034   QList<QByteArray> rv;
01035   foreach ( Types::AddrSpec addr, d_func()->msgIdList ) {
01036     rv.append( addr.asString().toLatin1() ); // FIXME change parsing to create QByteArrays
01037   }
01038   return rv;
01039 }
01040 
01041 void Ident::appendIdentifier( const QByteArray &id )
01042 {
01043   Q_D(Ident);
01044   QByteArray tmp = id;
01045   if ( !tmp.startsWith( '<' ) ) {
01046     tmp.prepend( '<' );
01047   }
01048   if ( !tmp.endsWith( '>' ) ) {
01049     tmp.append( '>' );
01050   }
01051   AddrSpec msgId;
01052   const char *cursor = tmp.constData();
01053   if ( parseAngleAddr( cursor, cursor + tmp.length(), msgId ) ) {
01054     d->msgIdList.append( msgId );
01055   } else {
01056     kWarning() << "Unable to parse address spec!";
01057   }
01058 }
01059 
01060 //-----</Ident>-------------------------
01061 
01062 //-----<SingleIdent>-------------------------
01063 
01064 //@cond PRIVATE
01065 kmime_mk_trivial_ctor_with_dptr( SingleIdent, Ident )
01066 kmime_mk_dptr_ctor( SingleIdent, Ident )
01067 //@endcond
01068 
01069 QByteArray SingleIdent::identifier() const
01070 {
01071   if ( d_func()->msgIdList.isEmpty() ) {
01072     return QByteArray();
01073   }
01074   return identifiers().first();
01075 }
01076 
01077 void SingleIdent::setIdentifier( const QByteArray &id )
01078 {
01079   Q_D(SingleIdent);
01080   d->msgIdList.clear();
01081   appendIdentifier( id );
01082 }
01083 
01084 bool SingleIdent::parse( const char* &scursor, const char * const send,
01085                          bool isCRLF )
01086 {
01087   Q_D(SingleIdent);
01088   if ( !Ident::parse( scursor, send, isCRLF ) ) {
01089     return false;
01090   }
01091 
01092   if ( d->msgIdList.count() > 1 ) {
01093     KMIME_WARN << "more than one msg-id in header "
01094                << "allowing only a single one!" << endl;
01095   }
01096   return true;
01097 }
01098 
01099 //-----</SingleIdent>-------------------------
01100 
01101 } // namespace Generics
01102 
01103 //-----<ReturnPath>-------------------------
01104 
01105 //@cond PRIVATE
01106 kmime_mk_trivial_ctor_with_name_and_dptr( ReturnPath, Generics::Address, Return-Path )
01107 //@endcond
01108 
01109 QByteArray ReturnPath::as7BitString( bool withHeaderType ) const
01110 {
01111   if ( isEmpty() ) {
01112     return QByteArray();
01113   }
01114 
01115   QByteArray rv;
01116   if ( withHeaderType ) {
01117     rv += typeIntro();
01118   }
01119   rv += '<' + d_func()->mailbox.as7BitString( d_func()->encCS ) + '>';
01120   return rv;
01121 }
01122 
01123 void ReturnPath::clear()
01124 {
01125   Q_D(ReturnPath);
01126   d->mailbox.setAddress( Types::AddrSpec() );
01127   d->mailbox.setName( QString() );
01128 }
01129 
01130 bool ReturnPath::isEmpty() const
01131 {
01132   const Q_D(ReturnPath);
01133   return !d->mailbox.hasAddress() && !d->mailbox.hasName();
01134 }
01135 
01136 bool ReturnPath::parse( const char* &scursor, const char * const send,
01137                         bool isCRLF )
01138 {
01139   Q_D(ReturnPath);
01140   eatCFWS( scursor, send, isCRLF );
01141   if ( scursor == send ) {
01142     return false;
01143   }
01144 
01145   const char * oldscursor = scursor;
01146 
01147   Mailbox maybeMailbox;
01148   if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
01149     // mailbox parsing failed, but check for empty brackets:
01150     scursor = oldscursor;
01151     if ( *scursor != '<' ) {
01152       return false;
01153     }
01154     scursor++;
01155     eatCFWS( scursor, send, isCRLF );
01156     if ( scursor == send || *scursor != '>' ) {
01157       return false;
01158     }
01159     scursor++;
01160 
01161     // prepare a Null mailbox:
01162     AddrSpec emptyAddrSpec;
01163     maybeMailbox.setName( QString() );
01164     maybeMailbox.setAddress( emptyAddrSpec );
01165   } else {
01166     // check that there was no display-name:
01167     if ( maybeMailbox.hasName() ) {
01168       KMIME_WARN << "display-name \"" << maybeMailbox.name()
01169                  << "\" in Return-Path!" << endl;
01170     }
01171   }
01172   d->mailbox = maybeMailbox;
01173 
01174   // see if that was all:
01175   eatCFWS( scursor, send, isCRLF );
01176   // and warn if it wasn't:
01177   if ( scursor != send ) {
01178     KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl;
01179   }
01180   return true;
01181 }
01182 
01183 //-----</ReturnPath>-------------------------
01184 
01185 //-----<Generic>-------------------------------
01186 
01187 // NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable.
01188 
01189 Generic::Generic() : Generics::Unstructured( new GenericPrivate )
01190 {
01191 }
01192 
01193 Generic::Generic( const char *t ) : Generics::Unstructured( new GenericPrivate )
01194 {
01195   setType( t );
01196 }
01197 
01198 Generic::Generic( const char *t, Content *p )
01199   : Generics::Unstructured( new GenericPrivate, p )
01200 {
01201   setType( t );
01202 }
01203 
01204 Generic::Generic( const char *t, Content *p, const QByteArray &s )
01205   : Generics::Unstructured( new GenericPrivate, p )
01206 {
01207   from7BitString( s );
01208   setType( t );
01209 }
01210 
01211 Generic::Generic( const char *t, Content *p, const QString &s, const QByteArray &cs )
01212   : Generics::Unstructured( new GenericPrivate, p )
01213 {
01214   fromUnicodeString( s, cs );
01215   setType( t );
01216 }
01217 
01218 Generic::~Generic()
01219 {
01220 }
01221 
01222 void Generic::clear()
01223 {
01224   Q_D(Generic);
01225   delete[] d->type;
01226   d->type = 0;
01227   Unstructured::clear();
01228 }
01229 
01230 bool Generic::isEmpty() const
01231 {
01232   return d_func()->type == 0 || Unstructured::isEmpty();
01233 }
01234 
01235 const char *Generic::type() const
01236 {
01237   return d_func()->type;
01238 }
01239 
01240 void Generic::setType( const char *type )
01241 {
01242   Q_D(Generic);
01243   if ( d->type ) {
01244     delete[] d->type;
01245   }
01246   if ( type ) {
01247     d->type = new char[strlen( type )+1];
01248     strcpy( d->type, type );
01249   } else {
01250     d->type = 0;
01251   }
01252 }
01253 
01254 //-----<Generic>-------------------------------
01255 
01256 //-----<MessageID>-----------------------------
01257 
01258 //@cond PRIVATE
01259 kmime_mk_trivial_ctor_with_name( MessageID, Generics::SingleIdent, Message-Id )
01260 //@endcond
01261 
01262 void MessageID::generate( const QByteArray &fqdn )
01263 {
01264   setIdentifier( uniqueString() + '@' + fqdn + '>' );
01265 }
01266 
01267 //-----</MessageID>----------------------------
01268 
01269 //-----<Control>-------------------------------
01270 
01271 //@cond PRIVATE
01272 kmime_mk_trivial_ctor_with_name_and_dptr( Control, Generics::Structured, Control )
01273 //@endcond
01274 
01275 QByteArray Control::as7BitString( bool withHeaderType ) const
01276 {
01277   const Q_D(Control);
01278   if ( isEmpty() ) {
01279     return QByteArray();
01280   }
01281 
01282   QByteArray rv;
01283   if ( withHeaderType ) {
01284     rv += typeIntro();
01285   }
01286 
01287   rv += d->name;
01288   if ( !d->parameter.isEmpty() ) {
01289     rv += ' ' + d->parameter;
01290   }
01291   return rv;
01292 }
01293 
01294 void Control::clear()
01295 {
01296   Q_D(Control);
01297   d->name.clear();
01298   d->parameter.clear();
01299 }
01300 
01301 bool Control::isEmpty() const
01302 {
01303   return d_func()->name.isEmpty();
01304 }
01305 
01306 QByteArray Control::controlType() const
01307 {
01308   return d_func()->name;
01309 }
01310 
01311 QByteArray Control::parameter() const
01312 {
01313   return d_func()->parameter;
01314 }
01315 
01316 bool Control::isCancel() const
01317 {
01318   return d_func()->name.toLower() == "cancel";
01319 }
01320 
01321 void Control::setCancel( const QByteArray &msgid )
01322 {
01323   Q_D(Control);
01324   d->name = "cancel";
01325   d->parameter = msgid;
01326 }
01327 
01328 bool Control::parse( const char* &scursor, const char *const send, bool isCRLF )
01329 {
01330   Q_D(Control);
01331   clear();
01332   eatCFWS( scursor, send, isCRLF );
01333   if ( scursor == send ) {
01334     return false;
01335   }
01336   const char *start = scursor;
01337   while ( scursor != send && !isspace( *scursor ) ) {
01338     ++scursor;
01339   }
01340   d->name = QByteArray( start, scursor - start );
01341   eatCFWS( scursor, send, isCRLF );
01342   d->parameter = QByteArray( scursor, send - scursor );
01343   return true;
01344 }
01345 
01346 //-----</Control>------------------------------
01347 
01348 //-----<MailCopiesTo>--------------------------
01349 
01350 //@cond PRIVATE
01351 kmime_mk_trivial_ctor_with_name_and_dptr( MailCopiesTo,
01352                                  Generics::AddressList, Mail-Copies-To )
01353 //@endcond
01354 
01355 QByteArray MailCopiesTo::as7BitString( bool withHeaderType ) const
01356 {
01357   QByteArray rv;
01358   if ( withHeaderType ) {
01359     rv += typeIntro();
01360   }
01361   if ( !AddressList::isEmpty() ) {
01362     rv += AddressList::as7BitString( false );
01363   } else {
01364     if ( d_func()->alwaysCopy ) {
01365       rv += "poster";
01366     } else if ( d_func()->neverCopy ) {
01367       rv += "nobody";
01368     }
01369   }
01370   return rv;
01371 }
01372 
01373 QString MailCopiesTo::asUnicodeString() const
01374 {
01375   if ( !AddressList::isEmpty() ) {
01376     return AddressList::asUnicodeString();
01377   }
01378   if ( d_func()->alwaysCopy ) {
01379     return QLatin1String( "poster" );
01380   }
01381   if ( d_func()->neverCopy ) {
01382     return QLatin1String( "nobody" );
01383   }
01384   return QString();
01385 }
01386 
01387 void MailCopiesTo::clear()
01388 {
01389   Q_D(MailCopiesTo);
01390   AddressList::clear();
01391   d->alwaysCopy = false;
01392   d->neverCopy = false;
01393 }
01394 
01395 bool MailCopiesTo::isEmpty() const
01396 {
01397   return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy);
01398 }
01399 
01400 bool MailCopiesTo::alwaysCopy() const
01401 {
01402   return !AddressList::isEmpty() || d_func()->alwaysCopy;
01403 }
01404 
01405 void MailCopiesTo::setAlwaysCopy()
01406 {
01407   Q_D(MailCopiesTo);
01408   clear();
01409   d->alwaysCopy = true;
01410 }
01411 
01412 bool MailCopiesTo::neverCopy() const
01413 {
01414   return d_func()->neverCopy;
01415 }
01416 
01417 void MailCopiesTo::setNeverCopy()
01418 {
01419   Q_D(MailCopiesTo);
01420   clear();
01421   d->neverCopy = true;
01422 }
01423 
01424 bool MailCopiesTo::parse( const char *& scursor, const char * const send,
01425                           bool isCRLF )
01426 {
01427   Q_D(MailCopiesTo);
01428   clear();
01429   if ( send - scursor == 5 ) {
01430     if ( qstrnicmp( "never", scursor, 5 ) == 0 ) {
01431       d->neverCopy = true;
01432       return true;
01433     }
01434   }
01435   if ( send - scursor == 6 ) {
01436     if ( qstrnicmp( "always", scursor, 6 ) == 0 || qstrnicmp( "poster", scursor, 6 ) == 0 ) {
01437       d->alwaysCopy = true;
01438       return true;
01439     }
01440     if ( qstrnicmp( "nobody", scursor, 6 ) == 0 ) {
01441       d->neverCopy = true;
01442       return true;
01443     }
01444   }
01445   return AddressList::parse( scursor, send, isCRLF );
01446 }
01447 
01448 //-----</MailCopiesTo>-------------------------
01449 
01450 //-----<Date>----------------------------------
01451 
01452 //@cond PRIVATE
01453 kmime_mk_trivial_ctor_with_name_and_dptr( Date, Generics::Structured, Date )
01454 //@endcond
01455 
01456 QByteArray Date::as7BitString( bool withHeaderType ) const
01457 {
01458   if ( isEmpty() ) {
01459     return QByteArray();
01460   }
01461 
01462   QByteArray rv;
01463   if ( withHeaderType ) {
01464     rv += typeIntro();
01465   }
01466   rv += d_func()->dateTime.toString( KDateTime::RFCDateDay ).toLatin1();
01467   return rv;
01468 }
01469 
01470 void Date::clear()
01471 {
01472   Q_D(Date);
01473   d->dateTime = KDateTime();
01474 }
01475 
01476 bool Date::isEmpty() const
01477 {
01478   return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid();
01479 }
01480 
01481 KDateTime Date::dateTime() const
01482 {
01483   return d_func()->dateTime;
01484 }
01485 
01486 void Date::setDateTime( const KDateTime &dt )
01487 {
01488   Q_D(Date);
01489   d->dateTime = dt;
01490 }
01491 
01492 int Date::ageInDays() const
01493 {
01494   QDate today = QDate::currentDate();
01495   return dateTime().date().daysTo(today);
01496 }
01497 
01498 bool Date::parse( const char* &scursor, const char *const send, bool isCRLF )
01499 {
01500   Q_D(Date);
01501   return parseDateTime( scursor, send, d->dateTime, isCRLF );
01502 }
01503 
01504 //-----</Date>---------------------------------
01505 
01506 //-----<Newsgroups>----------------------------
01507 
01508 //@cond PRIVATE
01509 kmime_mk_trivial_ctor_with_name_and_dptr( Newsgroups, Generics::Structured, Newsgroups )
01510 kmime_mk_trivial_ctor_with_name( FollowUpTo, Newsgroups, Followup-To )
01511 //@endcond
01512 
01513 QByteArray Newsgroups::as7BitString( bool withHeaderType ) const
01514 {
01515   const Q_D(Newsgroups);
01516   if ( isEmpty() ) {
01517     return QByteArray();
01518   }
01519 
01520   QByteArray rv;
01521   if ( withHeaderType ) {
01522     rv += typeIntro();
01523   }
01524 
01525   for ( int i = 0; i < d->groups.count(); ++i ) {
01526     rv += d->groups[ i ];
01527     if ( i != d->groups.count() - 1 ) {
01528       rv += ',';
01529     }
01530   }
01531   return rv;
01532 }
01533 
01534 void Newsgroups::fromUnicodeString( const QString &s, const QByteArray &b )
01535 {
01536   Q_UNUSED( b );
01537   Q_D(Newsgroups);
01538   from7BitString( s.toUtf8() );
01539   d->encCS = cachedCharset( "UTF-8" );
01540 }
01541 
01542 QString Newsgroups::asUnicodeString() const
01543 {
01544   return QString::fromUtf8( as7BitString( false ) );
01545 }
01546 
01547 void Newsgroups::clear()
01548 {
01549   Q_D(Newsgroups);
01550   d->groups.clear();
01551 }
01552 
01553 bool Newsgroups::isEmpty() const
01554 {
01555   return d_func()->groups.isEmpty();
01556 }
01557 
01558 QList<QByteArray> Newsgroups::groups() const
01559 {
01560   return d_func()->groups;
01561 }
01562 
01563 void Newsgroups::setGroups( const QList<QByteArray> &groups )
01564 {
01565   Q_D(Newsgroups);
01566   d->groups = groups;
01567 }
01568 
01569 bool Newsgroups::isCrossposted() const
01570 {
01571   return d_func()->groups.count() >= 2;
01572 }
01573 
01574 bool Newsgroups::parse( const char* &scursor, const char *const send, bool isCRLF )
01575 {
01576   Q_D(Newsgroups);
01577   clear();
01578   forever {
01579     eatCFWS( scursor, send, isCRLF );
01580     if ( scursor != send && *scursor == ',' ) {
01581       ++scursor;
01582     }
01583     eatCFWS( scursor, send, isCRLF );
01584     if ( scursor == send ) {
01585       return true;
01586     }
01587     const char *start = scursor;
01588     while ( scursor != send && !isspace( *scursor ) && *scursor != ',' ) {
01589       ++scursor;
01590     }
01591     QByteArray group( start, scursor - start );
01592     d->groups.append( group );
01593   }
01594   return true;
01595 }
01596 
01597 //-----</Newsgroups>---------------------------
01598 
01599 //-----<Lines>---------------------------------
01600 
01601 //@cond PRIVATE
01602 kmime_mk_trivial_ctor_with_name_and_dptr( Lines, Generics::Structured, Lines )
01603 //@endcond
01604 
01605 QByteArray Lines::as7BitString( bool withHeaderType ) const
01606 {
01607   if ( isEmpty() ) {
01608     return QByteArray();
01609   }
01610 
01611   QByteArray num;
01612   num.setNum( d_func()->lines );
01613 
01614   if ( withHeaderType ) {
01615     return typeIntro() + num;
01616   }
01617   return num;
01618 }
01619 
01620 QString Lines::asUnicodeString() const
01621 {
01622   if ( isEmpty() ) {
01623     return QString();
01624   }
01625   return QString::number( d_func()->lines );
01626 }
01627 
01628 void Lines::clear()
01629 {
01630   Q_D(Lines);
01631   d->lines = -1;
01632 }
01633 
01634 bool Lines::isEmpty() const
01635 {
01636   return d_func()->lines == -1;
01637 }
01638 
01639 int Lines::numberOfLines() const
01640 {
01641   return d_func()->lines;
01642 }
01643 
01644 void Lines::setNumberOfLines( int lines )
01645 {
01646   Q_D(Lines);
01647   d->lines = lines;
01648 }
01649 
01650 bool Lines::parse( const char* &scursor, const char* const send, bool isCRLF )
01651 {
01652   Q_D(Lines);
01653   eatCFWS( scursor, send, isCRLF );
01654   if ( parseDigits( scursor, send, d->lines )  == 0 ) {
01655     clear();
01656     return false;
01657   }
01658   return true;
01659 }
01660 
01661 //-----</Lines>--------------------------------
01662 
01663 //-----<Content-Type>--------------------------
01664 
01665 //@cond PRIVATE
01666 kmime_mk_trivial_ctor_with_name_and_dptr( ContentType, Generics::Parametrized,
01667                                  Content-Type )
01668 //@endcond
01669 
01670 bool ContentType::isEmpty() const
01671 {
01672   return d_func()->mimeType.isEmpty();
01673 }
01674 
01675 void ContentType::clear()
01676 {
01677   Q_D(ContentType);
01678   d->category = CCsingle;
01679   d->mimeType.clear();
01680   d->mimeSubType.clear();
01681   Parametrized::clear();
01682 }
01683 
01684 QByteArray ContentType::as7BitString( bool withHeaderType ) const
01685 {
01686   if ( isEmpty() ) {
01687     return QByteArray();
01688   }
01689 
01690   QByteArray rv;
01691   if ( withHeaderType ) {
01692     rv += typeIntro();
01693   }
01694 
01695   rv += mimeType();
01696   if ( !Parametrized::isEmpty() ) {
01697     rv += "; " + Parametrized::as7BitString( false );
01698   }
01699 
01700   return rv;
01701 }
01702 
01703 QByteArray ContentType::mimeType() const
01704 {
01705   return d_func()->mimeType + '/' + d_func()->mimeSubType;
01706 }
01707 
01708 QByteArray ContentType::mediaType() const
01709 {
01710   return d_func()->mimeType;
01711 }
01712 
01713 QByteArray ContentType::subType() const
01714 {
01715   return d_func()->mimeSubType;
01716 }
01717 
01718 void ContentType::setMimeType( const QByteArray &mimeType )
01719 {
01720   Q_D(ContentType);
01721   int pos = mimeType.indexOf( '/' );
01722   if ( pos < 0 ) {
01723     d->mimeType = mimeType;
01724     d->mimeSubType.clear();
01725   } else {
01726     d->mimeType = mimeType.left( pos );
01727     d->mimeSubType = mimeType.mid( pos + 1 );
01728   }
01729   Parametrized::clear();
01730 
01731   if ( isMultipart() ) {
01732     d->category = CCcontainer;
01733   } else {
01734     d->category = CCsingle;
01735   }
01736 }
01737 
01738 bool ContentType::isMediatype( const char *mediatype ) const
01739 {
01740   return strncasecmp( mediaType().constData(), mediatype, strlen( mediatype ) ) == 0;
01741 }
01742 
01743 bool ContentType::isSubtype( const char *subtype ) const
01744 {
01745   return strncasecmp( subType().constData(), subtype, strlen( subtype ) ) == 0;
01746 }
01747 
01748 bool ContentType::isText() const
01749 {
01750   return ( strncasecmp( mediaType().constData(), "text", 4 ) == 0
01751           || isEmpty() );
01752 }
01753 
01754 bool ContentType::isPlainText() const
01755 {
01756   return ( strcasecmp( mimeType().constData(), "text/plain" ) == 0
01757           || isEmpty() );
01758 }
01759 
01760 bool ContentType::isHTMLText() const
01761 {
01762   return strcasecmp( mimeType().constData(), "text/html" ) == 0;
01763 }
01764 
01765 bool ContentType::isImage() const
01766 {
01767   return strncasecmp( mediaType().constData(), "image", 5 ) == 0;
01768 }
01769 
01770 bool ContentType::isMultipart() const
01771 {
01772   return strncasecmp( mediaType().constData(), "multipart", 9 ) == 0;
01773 }
01774 
01775 bool ContentType::isPartial() const
01776 {
01777   return strcasecmp( mimeType().constData(), "message/partial" ) == 0;
01778 }
01779 
01780 QByteArray ContentType::charset() const
01781 {
01782   QByteArray ret = parameter( "charset" ).toLatin1();
01783   if ( ret.isEmpty() || forceDefaultCharset() ) {
01784     //return the default-charset if necessary
01785     ret = defaultCharset();
01786   }
01787   return ret;
01788 }
01789 
01790 void ContentType::setCharset( const QByteArray &s )
01791 {
01792   setParameter( "charset", QString::fromLatin1( s ) );
01793 }
01794 
01795 QByteArray ContentType::boundary() const
01796 {
01797   return parameter( "boundary" ).toLatin1();
01798 }
01799 
01800 void ContentType::setBoundary( const QByteArray &s )
01801 {
01802   setParameter( "boundary", QString::fromLatin1( s ) );
01803 }
01804 
01805 QString ContentType::name() const
01806 {
01807   return parameter( "name" );
01808 }
01809 
01810 void ContentType::setName( const QString &s, const QByteArray &cs )
01811 {
01812   Q_D(ContentType);
01813   d->encCS = cs;
01814   setParameter( "name", s );
01815 }
01816 
01817 QByteArray ContentType::id() const
01818 {
01819   return parameter( "id" ).toLatin1();
01820 }
01821 
01822 void ContentType::setId( const QByteArray &s )
01823 {
01824   setParameter( "id", s );
01825 }
01826 
01827 int ContentType::partialNumber() const
01828 {
01829   QByteArray p = parameter( "number" ).toLatin1();
01830   if ( !p.isEmpty() ) {
01831     return p.toInt();
01832   } else {
01833     return -1;
01834   }
01835 }
01836 
01837 int ContentType::partialCount() const
01838 {
01839   QByteArray p = parameter( "total" ).toLatin1();
01840   if ( !p.isEmpty() ) {
01841     return p.toInt();
01842   } else {
01843     return -1;
01844   }
01845 }
01846 
01847 contentCategory ContentType::category() const
01848 {
01849   return d_func()->category;
01850 }
01851 
01852 void ContentType::setCategory( contentCategory c )
01853 {
01854   Q_D(ContentType);
01855   d->category = c;
01856 }
01857 
01858 void ContentType::setPartialParams( int total, int number )
01859 {
01860   setParameter( "number", QString::number( number ) );
01861   setParameter( "total", QString::number( total ) );
01862 }
01863 
01864 bool ContentType::parse( const char* &scursor, const char * const send,
01865                          bool isCRLF )
01866 {
01867   Q_D(ContentType);
01868   // content-type: type "/" subtype *(";" parameter)
01869 
01870   clear();
01871   eatCFWS( scursor, send, isCRLF );
01872   if ( scursor == send ) {
01873     return false; // empty header
01874   }
01875 
01876   // type
01877   QPair<const char*,int> maybeMimeType;
01878   if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) {
01879     return false;
01880   }
01881   d->mimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower();
01882 
01883   // subtype
01884   eatCFWS( scursor, send, isCRLF );
01885   if ( scursor == send || *scursor != '/' ) {
01886     return false;
01887   }
01888   scursor++;
01889   eatCFWS( scursor, send, isCRLF );
01890   if ( scursor == send ) {
01891     return false;
01892   }
01893 
01894   QPair<const char*,int> maybeSubType;
01895   if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) {
01896     return false;
01897   }
01898   d->mimeSubType = QByteArray( maybeSubType.first, maybeSubType.second ).toLower();
01899 
01900   // parameter list
01901   eatCFWS( scursor, send, isCRLF );
01902   if ( scursor == send ) {
01903     goto success; // no parameters
01904   }
01905 
01906   if ( *scursor != ';' ) {
01907     return false;
01908   }
01909   scursor++;
01910 
01911   if ( !Parametrized::parse( scursor, send, isCRLF ) ) {
01912     return false;
01913   }
01914 
01915   // adjust category
01916 success:
01917   if ( isMultipart() ) {
01918     d->category = CCcontainer;
01919   } else {
01920     d->category = CCsingle;
01921   }
01922   return true;
01923 }
01924 
01925 //-----</Content-Type>-------------------------
01926 
01927 //-----<ContentID>----------------------
01928 
01929 kmime_mk_trivial_ctor_with_name_and_dptr( ContentID, SingleIdent, Content-ID )
01930 kmime_mk_dptr_ctor( ContentID, SingleIdent )
01931 
01932 bool ContentID::parse( const char* &scursor, const char *const send, bool isCRLF )
01933 {
01934   Q_D ( ContentID );
01935   // Content-id := "<" contentid ">"
01936   // contentid := now whitespaces
01937 
01938   const char* origscursor = scursor;
01939   if ( !SingleIdent::parse ( scursor, send, isCRLF ) )
01940   {
01941     scursor = origscursor;
01942     d->msgIdList.clear();
01943 
01944     while ( scursor != send )
01945     {
01946       eatCFWS ( scursor, send, isCRLF );
01947       // empty entry ending the list: OK.
01948       if ( scursor == send )
01949       {
01950         return true;
01951       }
01952       // empty entry: ignore.
01953       if ( *scursor == ',' )
01954       {
01955         scursor++;
01956         continue;
01957       }
01958 
01959       AddrSpec maybeContentId;
01960       // Almost parseAngleAddr
01961       if ( scursor == send || *scursor != '<' )
01962       {
01963         return false;
01964       }
01965       scursor++; // eat '<'
01966 
01967       eatCFWS ( scursor, send, isCRLF );
01968       if ( scursor == send )
01969       {
01970         return false;
01971       }
01972 
01973       // Save chars untill '>''
01974       QString result = "";
01975       if( !parseAtom(scursor, send, result, false) ) {
01976         return false;
01977       }
01978 
01979       eatCFWS ( scursor, send, isCRLF );
01980       if ( scursor == send || *scursor != '>' )
01981       {
01982         return false;
01983       }
01984       scursor++;
01985       // /Almost parseAngleAddr
01986 
01987       maybeContentId.localPart = result;
01988       d->msgIdList.append ( maybeContentId );
01989 
01990       eatCFWS ( scursor, send, isCRLF );
01991       // header end ending the list: OK.
01992       if ( scursor == send )
01993       {
01994         return true;
01995       }
01996       // regular item separator: eat it.
01997       if ( *scursor == ',' )
01998       {
01999         scursor++;
02000       }
02001     }
02002     return true;
02003   }
02004   else
02005   {
02006     return true;
02007   }
02008 }
02009 
02010 //-----</ContentID>----------------------
02011 
02012 //-----<ContentTransferEncoding>----------------------------
02013 
02014 //@cond PRIVATE
02015 kmime_mk_trivial_ctor_with_name_and_dptr( ContentTransferEncoding,
02016                                  Generics::Token, Content-Transfer-Encoding )
02017 //@endcond
02018 
02019 typedef struct { const char *s; int e; } encTableType;
02020 
02021 static const encTableType encTable[] =
02022 {
02023   { "7Bit", CE7Bit },
02024   { "8Bit", CE8Bit },
02025   { "quoted-printable", CEquPr },
02026   { "base64", CEbase64 },
02027   { "x-uuencode", CEuuenc },
02028   { "binary", CEbinary },
02029   { 0, 0}
02030 };
02031 
02032 void ContentTransferEncoding::clear()
02033 {
02034   Q_D(ContentTransferEncoding);
02035   d->decoded = true;
02036   d->cte = CE7Bit;
02037   Token::clear();
02038 }
02039 
02040 contentEncoding ContentTransferEncoding::encoding() const
02041 {
02042   return d_func()->cte;
02043 }
02044 
02045 void ContentTransferEncoding::setEncoding( contentEncoding e )
02046 {
02047   Q_D(ContentTransferEncoding);
02048   d->cte = e;
02049 
02050   for ( int i = 0; encTable[i].s != 0; ++i ) {
02051     if ( d->cte == encTable[i].e ) {
02052       setToken( encTable[i].s );
02053       break;
02054     }
02055   }
02056 }
02057 
02058 bool ContentTransferEncoding::decoded() const
02059 {
02060   return d_func()->decoded;
02061 }
02062 
02063 void ContentTransferEncoding::setDecoded( bool decoded )
02064 {
02065   Q_D(ContentTransferEncoding);
02066   d->decoded = decoded;
02067 }
02068 
02069 bool ContentTransferEncoding::needToEncode() const
02070 {
02071   const Q_D(ContentTransferEncoding);
02072   return d->decoded && (d->cte == CEquPr || d->cte == CEbase64);
02073 }
02074 
02075 bool ContentTransferEncoding::parse( const char *& scursor,
02076                                      const char * const send, bool isCRLF )
02077 {
02078   Q_D(ContentTransferEncoding);
02079   clear();
02080   if ( !Token::parse( scursor, send, isCRLF ) ) {
02081     return false;
02082   }
02083 
02084   // TODO: error handling in case of an unknown encoding?
02085   for ( int i = 0; encTable[i].s != 0; ++i ) {
02086     if ( strcasecmp( token().constData(), encTable[i].s ) == 0 ) {
02087       d->cte = ( contentEncoding )encTable[i].e;
02088       break;
02089     }
02090   }
02091   d->decoded = ( d->cte == CE7Bit || d->cte == CE8Bit );
02092   return true;
02093 }
02094 
02095 //-----</ContentTransferEncoding>---------------------------
02096 
02097 //-----<ContentDisposition>--------------------------
02098 
02099 //@cond PRIVATE
02100 kmime_mk_trivial_ctor_with_name_and_dptr( ContentDisposition,
02101                                  Generics::Parametrized, Content-Disposition )
02102 //@endcond
02103 
02104 QByteArray ContentDisposition::as7BitString( bool withHeaderType ) const
02105 {
02106   if ( isEmpty() ) {
02107     return QByteArray();
02108   }
02109 
02110   QByteArray rv;
02111   if ( withHeaderType ) {
02112     rv += typeIntro();
02113   }
02114 
02115   if ( d_func()->disposition == CDattachment ) {
02116     rv += "attachment";
02117   } else if ( d_func()->disposition == CDinline ) {
02118     rv += "inline";
02119   } else {
02120     return QByteArray();
02121   }
02122 
02123   if ( !Parametrized::isEmpty() ) {
02124     rv += "; " + Parametrized::as7BitString( false );
02125   }
02126 
02127   return rv;
02128 }
02129 
02130 bool ContentDisposition::isEmpty() const
02131 {
02132   return d_func()->disposition == CDInvalid;
02133 }
02134 
02135 void ContentDisposition::clear()
02136 {
02137   Q_D(ContentDisposition);
02138   d->disposition = CDInvalid;
02139   Parametrized::clear();
02140 }
02141 
02142 contentDisposition ContentDisposition::disposition() const
02143 {
02144   return d_func()->disposition;
02145 }
02146 
02147 void ContentDisposition::setDisposition( contentDisposition disp )
02148 {
02149   Q_D(ContentDisposition);
02150   d->disposition = disp;
02151 }
02152 
02153 QString KMime::Headers::ContentDisposition::filename() const
02154 {
02155   return parameter( "filename" );
02156 }
02157 
02158 void ContentDisposition::setFilename( const QString &filename )
02159 {
02160   setParameter( "filename", filename );
02161 }
02162 
02163 bool ContentDisposition::parse( const char *& scursor, const char * const send,
02164                                 bool isCRLF )
02165 {
02166   Q_D(ContentDisposition);
02167   clear();
02168 
02169   // token
02170   QByteArray token;
02171   eatCFWS( scursor, send, isCRLF );
02172   if ( scursor == send ) {
02173     return false;
02174   }
02175 
02176   QPair<const char*,int> maybeToken;
02177   if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) {
02178     return false;
02179   }
02180 
02181   token = QByteArray( maybeToken.first, maybeToken.second ).toLower();
02182   if ( token == "inline" ) {
02183     d->disposition = CDinline;
02184   } else if ( token == "attachment" ) {
02185     d->disposition = CDattachment;
02186   } else {
02187     return false;
02188   }
02189 
02190   // parameter list
02191   eatCFWS( scursor, send, isCRLF );
02192   if ( scursor == send ) {
02193     return true; // no parameters
02194   }
02195 
02196   if ( *scursor != ';' ) {
02197     return false;
02198   }
02199   scursor++;
02200 
02201   return Parametrized::parse( scursor, send, isCRLF );
02202 }
02203 
02204 //-----</ContentDisposition>-------------------------
02205 
02206 //@cond PRIVATE
02207 kmime_mk_trivial_ctor_with_name( Subject, Generics::Unstructured, Subject )
02208 //@endcond
02209 
02210 bool Subject::isReply() const
02211 {
02212   return asUnicodeString().indexOf( QLatin1String( "Re:" ), 0, Qt::CaseInsensitive ) == 0;
02213 }
02214 
02215 //@cond PRIVATE
02216 kmime_mk_trivial_ctor_with_name( ContentDescription,
02217                                  Generics::Unstructured, Content-Description )
02218 kmime_mk_trivial_ctor_with_name( ContentLocation,
02219                                 Generics::Unstructured, Content-Location )
02220 kmime_mk_trivial_ctor_with_name( From, Generics::MailboxList, From )
02221 kmime_mk_trivial_ctor_with_name( Sender, Generics::SingleMailbox, Sender )
02222 kmime_mk_trivial_ctor_with_name( To, Generics::AddressList, To )
02223 kmime_mk_trivial_ctor_with_name( Cc, Generics::AddressList, Cc )
02224 kmime_mk_trivial_ctor_with_name( Bcc, Generics::AddressList, Bcc )
02225 kmime_mk_trivial_ctor_with_name( ReplyTo, Generics::AddressList, Reply-To )
02226 kmime_mk_trivial_ctor_with_name( Keywords, Generics::PhraseList, Keywords )
02227 kmime_mk_trivial_ctor_with_name( MIMEVersion, Generics::DotAtom, MIME-Version )
02228 kmime_mk_trivial_ctor_with_name( Supersedes, Generics::SingleIdent, Supersedes )
02229 kmime_mk_trivial_ctor_with_name( InReplyTo, Generics::Ident, In-Reply-To )
02230 kmime_mk_trivial_ctor_with_name( References, Generics::Ident, References )
02231 kmime_mk_trivial_ctor_with_name( Organization, Generics::Unstructured, Organization )
02232 kmime_mk_trivial_ctor_with_name( UserAgent, Generics::Unstructured, User-Agent )
02233 //@endcond
02234 
02235 } // namespace Headers
02236 
02237 } // namespace KMime

KMIME Library

Skip menu "KMIME Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal