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 ( const 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 ( const 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 ( const 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 ( const 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 ( const Types::Address &addr, d->addressList ) { 00549 foreach ( const 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 ( const Types::Address &addr, d_func()->addressList ) { 00605 foreach ( const 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 ( const Types::Address &addr, d_func()->addressList ) { 00616 foreach ( const 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 ( const Types::Address &addr, d_func()->addressList ) { 00627 foreach ( const 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 ( const Types::Address &addr, d_func()->addressList ) { 00638 foreach ( const 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 if ( isUsAscii( it.value() ) ) { 00903 rv += it.key().toLatin1() + '='; 00904 QByteArray tmp = it.value().toLatin1(); 00905 addQuotes( tmp, true ); // force quoting, eg. for whitespaces in parameter value 00906 rv += tmp; 00907 } else { 00908 if( useOutlookAttachmentEncoding() ) { 00909 rv += it.key().toLatin1() + '='; 00910 kDebug() << "doing:" << it.value() << QLatin1String( d->encCS ); 00911 rv += "\"" + encodeRFC2047String( it.value(), d->encCS ) + "\""; 00912 } else { 00913 rv += it.key().toLatin1() + "*="; 00914 rv += encodeRFC2231String( it.value(), d->encCS ); 00915 } 00916 } 00917 } 00918 00919 return rv; 00920 } 00921 00922 QString Parametrized::parameter( const QString &key ) const 00923 { 00924 return d_func()->parameterHash.value( key.toLower() ); 00925 } 00926 00927 bool Parametrized::hasParameter( const QString &key ) const 00928 { 00929 return d_func()->parameterHash.contains( key.toLower() ); 00930 } 00931 00932 void Parametrized::setParameter( const QString &key, const QString &value ) 00933 { 00934 Q_D(Parametrized); 00935 d->parameterHash.insert( key.toLower(), value ); 00936 } 00937 00938 bool Parametrized::isEmpty() const 00939 { 00940 return d_func()->parameterHash.isEmpty(); 00941 } 00942 00943 void Parametrized::clear() 00944 { 00945 Q_D(Parametrized); 00946 d->parameterHash.clear(); 00947 } 00948 00949 bool Parametrized::parse( const char *& scursor, const char * const send, 00950 bool isCRLF ) 00951 { 00952 Q_D(Parametrized); 00953 d->parameterHash.clear(); 00954 QByteArray charset; 00955 if ( !parseParameterListWithCharset( scursor, send, d->parameterHash, charset, isCRLF ) ) { 00956 return false; 00957 } 00958 d->encCS = charset; 00959 return true; 00960 } 00961 00962 //-----</Parametrized>------------------------- 00963 00964 //-----<Ident>------------------------- 00965 00966 //@cond PRIVATE 00967 kmime_mk_trivial_ctor_with_dptr( Ident, Address ) 00968 kmime_mk_dptr_ctor( Ident, Address ) 00969 //@endcond 00970 00971 QByteArray Ident::as7BitString( bool withHeaderType ) const 00972 { 00973 const Q_D(Ident); 00974 if ( d->msgIdList.isEmpty() ) { 00975 return QByteArray(); 00976 } 00977 00978 QByteArray rv; 00979 if ( withHeaderType ) { 00980 rv = typeIntro(); 00981 } 00982 foreach ( const Types::AddrSpec &addr, d->msgIdList ) { 00983 rv += '<'; 00984 rv += addr.asString().toLatin1(); // FIXME: change parsing to use QByteArrays 00985 rv += "> "; 00986 } 00987 rv.resize( rv.length() - 1 ); 00988 return rv; 00989 } 00990 00991 void Ident::clear() 00992 { 00993 Q_D(Ident); 00994 d->msgIdList.clear(); 00995 } 00996 00997 bool Ident::isEmpty() const 00998 { 00999 return d_func()->msgIdList.isEmpty(); 01000 } 01001 01002 bool Ident::parse( const char* &scursor, const char * const send, bool isCRLF ) 01003 { 01004 Q_D(Ident); 01005 // msg-id := "<" id-left "@" id-right ">" 01006 // id-left := dot-atom-text / no-fold-quote / local-part 01007 // id-right := dot-atom-text / no-fold-literal / domain 01008 // 01009 // equivalent to: 01010 // msg-id := angle-addr 01011 01012 d->msgIdList.clear(); 01013 01014 while ( scursor != send ) { 01015 eatCFWS( scursor, send, isCRLF ); 01016 // empty entry ending the list: OK. 01017 if ( scursor == send ) { 01018 return true; 01019 } 01020 // empty entry: ignore. 01021 if ( *scursor == ',' ) { 01022 scursor++; 01023 continue; 01024 } 01025 01026 AddrSpec maybeMsgId; 01027 if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) { 01028 return false; 01029 } 01030 d->msgIdList.append( maybeMsgId ); 01031 01032 eatCFWS( scursor, send, isCRLF ); 01033 // header end ending the list: OK. 01034 if ( scursor == send ) { 01035 return true; 01036 } 01037 // regular item separator: eat it. 01038 if ( *scursor == ',' ) { 01039 scursor++; 01040 } 01041 } 01042 return true; 01043 } 01044 01045 QList<QByteArray> Ident::identifiers() const 01046 { 01047 QList<QByteArray> rv; 01048 foreach ( const Types::AddrSpec &addr, d_func()->msgIdList ) { 01049 rv.append( addr.asString().toLatin1() ); // FIXME change parsing to create QByteArrays 01050 } 01051 return rv; 01052 } 01053 01054 void Ident::appendIdentifier( const QByteArray &id ) 01055 { 01056 Q_D(Ident); 01057 QByteArray tmp = id; 01058 if ( !tmp.startsWith( '<' ) ) { 01059 tmp.prepend( '<' ); 01060 } 01061 if ( !tmp.endsWith( '>' ) ) { 01062 tmp.append( '>' ); 01063 } 01064 AddrSpec msgId; 01065 const char *cursor = tmp.constData(); 01066 if ( parseAngleAddr( cursor, cursor + tmp.length(), msgId ) ) { 01067 d->msgIdList.append( msgId ); 01068 } else { 01069 kWarning() << "Unable to parse address spec!"; 01070 } 01071 } 01072 01073 //-----</Ident>------------------------- 01074 01075 //-----<SingleIdent>------------------------- 01076 01077 //@cond PRIVATE 01078 kmime_mk_trivial_ctor_with_dptr( SingleIdent, Ident ) 01079 kmime_mk_dptr_ctor( SingleIdent, Ident ) 01080 //@endcond 01081 01082 QByteArray SingleIdent::identifier() const 01083 { 01084 if ( d_func()->msgIdList.isEmpty() ) { 01085 return QByteArray(); 01086 } 01087 return identifiers().first(); 01088 } 01089 01090 void SingleIdent::setIdentifier( const QByteArray &id ) 01091 { 01092 Q_D(SingleIdent); 01093 d->msgIdList.clear(); 01094 appendIdentifier( id ); 01095 } 01096 01097 bool SingleIdent::parse( const char* &scursor, const char * const send, 01098 bool isCRLF ) 01099 { 01100 Q_D(SingleIdent); 01101 if ( !Ident::parse( scursor, send, isCRLF ) ) { 01102 return false; 01103 } 01104 01105 if ( d->msgIdList.count() > 1 ) { 01106 KMIME_WARN << "more than one msg-id in header " 01107 << "allowing only a single one!" << endl; 01108 } 01109 return true; 01110 } 01111 01112 //-----</SingleIdent>------------------------- 01113 01114 } // namespace Generics 01115 01116 //-----<ReturnPath>------------------------- 01117 01118 //@cond PRIVATE 01119 kmime_mk_trivial_ctor_with_name_and_dptr( ReturnPath, Generics::Address, Return-Path ) 01120 //@endcond 01121 01122 QByteArray ReturnPath::as7BitString( bool withHeaderType ) const 01123 { 01124 if ( isEmpty() ) { 01125 return QByteArray(); 01126 } 01127 01128 QByteArray rv; 01129 if ( withHeaderType ) { 01130 rv += typeIntro(); 01131 } 01132 rv += '<' + d_func()->mailbox.as7BitString( d_func()->encCS ) + '>'; 01133 return rv; 01134 } 01135 01136 void ReturnPath::clear() 01137 { 01138 Q_D(ReturnPath); 01139 d->mailbox.setAddress( Types::AddrSpec() ); 01140 d->mailbox.setName( QString() ); 01141 } 01142 01143 bool ReturnPath::isEmpty() const 01144 { 01145 const Q_D(ReturnPath); 01146 return !d->mailbox.hasAddress() && !d->mailbox.hasName(); 01147 } 01148 01149 bool ReturnPath::parse( const char* &scursor, const char * const send, 01150 bool isCRLF ) 01151 { 01152 Q_D(ReturnPath); 01153 eatCFWS( scursor, send, isCRLF ); 01154 if ( scursor == send ) { 01155 return false; 01156 } 01157 01158 const char * oldscursor = scursor; 01159 01160 Mailbox maybeMailbox; 01161 if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) { 01162 // mailbox parsing failed, but check for empty brackets: 01163 scursor = oldscursor; 01164 if ( *scursor != '<' ) { 01165 return false; 01166 } 01167 scursor++; 01168 eatCFWS( scursor, send, isCRLF ); 01169 if ( scursor == send || *scursor != '>' ) { 01170 return false; 01171 } 01172 scursor++; 01173 01174 // prepare a Null mailbox: 01175 AddrSpec emptyAddrSpec; 01176 maybeMailbox.setName( QString() ); 01177 maybeMailbox.setAddress( emptyAddrSpec ); 01178 } else { 01179 // check that there was no display-name: 01180 if ( maybeMailbox.hasName() ) { 01181 KMIME_WARN << "display-name \"" << maybeMailbox.name() 01182 << "\" in Return-Path!" << endl; 01183 } 01184 } 01185 d->mailbox = maybeMailbox; 01186 01187 // see if that was all: 01188 eatCFWS( scursor, send, isCRLF ); 01189 // and warn if it wasn't: 01190 if ( scursor != send ) { 01191 KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl; 01192 } 01193 return true; 01194 } 01195 01196 //-----</ReturnPath>------------------------- 01197 01198 //-----<Generic>------------------------------- 01199 01200 // NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable. 01201 01202 Generic::Generic() : Generics::Unstructured( new GenericPrivate ) 01203 { 01204 } 01205 01206 Generic::Generic( const char *t ) : Generics::Unstructured( new GenericPrivate ) 01207 { 01208 setType( t ); 01209 } 01210 01211 Generic::Generic( const char *t, Content *p ) 01212 : Generics::Unstructured( new GenericPrivate, p ) 01213 { 01214 setType( t ); 01215 } 01216 01217 Generic::Generic( const char *t, Content *p, const QByteArray &s ) 01218 : Generics::Unstructured( new GenericPrivate, p ) 01219 { 01220 from7BitString( s ); 01221 setType( t ); 01222 } 01223 01224 Generic::Generic( const char *t, Content *p, const QString &s, const QByteArray &cs ) 01225 : Generics::Unstructured( new GenericPrivate, p ) 01226 { 01227 fromUnicodeString( s, cs ); 01228 setType( t ); 01229 } 01230 01231 Generic::~Generic() 01232 { 01233 } 01234 01235 void Generic::clear() 01236 { 01237 Q_D(Generic); 01238 delete[] d->type; 01239 d->type = 0; 01240 Unstructured::clear(); 01241 } 01242 01243 bool Generic::isEmpty() const 01244 { 01245 return d_func()->type == 0 || Unstructured::isEmpty(); 01246 } 01247 01248 const char *Generic::type() const 01249 { 01250 return d_func()->type; 01251 } 01252 01253 void Generic::setType( const char *type ) 01254 { 01255 Q_D(Generic); 01256 if ( d->type ) { 01257 delete[] d->type; 01258 } 01259 if ( type ) { 01260 d->type = new char[strlen( type )+1]; 01261 strcpy( d->type, type ); 01262 } else { 01263 d->type = 0; 01264 } 01265 } 01266 01267 //-----<Generic>------------------------------- 01268 01269 //-----<MessageID>----------------------------- 01270 01271 //@cond PRIVATE 01272 kmime_mk_trivial_ctor_with_name( MessageID, Generics::SingleIdent, Message-Id ) 01273 //@endcond 01274 01275 void MessageID::generate( const QByteArray &fqdn ) 01276 { 01277 setIdentifier( uniqueString() + '@' + fqdn + '>' ); 01278 } 01279 01280 //-----</MessageID>---------------------------- 01281 01282 //-----<Control>------------------------------- 01283 01284 //@cond PRIVATE 01285 kmime_mk_trivial_ctor_with_name_and_dptr( Control, Generics::Structured, Control ) 01286 //@endcond 01287 01288 QByteArray Control::as7BitString( bool withHeaderType ) const 01289 { 01290 const Q_D(Control); 01291 if ( isEmpty() ) { 01292 return QByteArray(); 01293 } 01294 01295 QByteArray rv; 01296 if ( withHeaderType ) { 01297 rv += typeIntro(); 01298 } 01299 01300 rv += d->name; 01301 if ( !d->parameter.isEmpty() ) { 01302 rv += ' ' + d->parameter; 01303 } 01304 return rv; 01305 } 01306 01307 void Control::clear() 01308 { 01309 Q_D(Control); 01310 d->name.clear(); 01311 d->parameter.clear(); 01312 } 01313 01314 bool Control::isEmpty() const 01315 { 01316 return d_func()->name.isEmpty(); 01317 } 01318 01319 QByteArray Control::controlType() const 01320 { 01321 return d_func()->name; 01322 } 01323 01324 QByteArray Control::parameter() const 01325 { 01326 return d_func()->parameter; 01327 } 01328 01329 bool Control::isCancel() const 01330 { 01331 return d_func()->name.toLower() == "cancel"; 01332 } 01333 01334 void Control::setCancel( const QByteArray &msgid ) 01335 { 01336 Q_D(Control); 01337 d->name = "cancel"; 01338 d->parameter = msgid; 01339 } 01340 01341 bool Control::parse( const char* &scursor, const char *const send, bool isCRLF ) 01342 { 01343 Q_D(Control); 01344 clear(); 01345 eatCFWS( scursor, send, isCRLF ); 01346 if ( scursor == send ) { 01347 return false; 01348 } 01349 const char *start = scursor; 01350 while ( scursor != send && !isspace( *scursor ) ) { 01351 ++scursor; 01352 } 01353 d->name = QByteArray( start, scursor - start ); 01354 eatCFWS( scursor, send, isCRLF ); 01355 d->parameter = QByteArray( scursor, send - scursor ); 01356 return true; 01357 } 01358 01359 //-----</Control>------------------------------ 01360 01361 //-----<MailCopiesTo>-------------------------- 01362 01363 //@cond PRIVATE 01364 kmime_mk_trivial_ctor_with_name_and_dptr( MailCopiesTo, 01365 Generics::AddressList, Mail-Copies-To ) 01366 //@endcond 01367 01368 QByteArray MailCopiesTo::as7BitString( bool withHeaderType ) const 01369 { 01370 QByteArray rv; 01371 if ( withHeaderType ) { 01372 rv += typeIntro(); 01373 } 01374 if ( !AddressList::isEmpty() ) { 01375 rv += AddressList::as7BitString( false ); 01376 } else { 01377 if ( d_func()->alwaysCopy ) { 01378 rv += "poster"; 01379 } else if ( d_func()->neverCopy ) { 01380 rv += "nobody"; 01381 } 01382 } 01383 return rv; 01384 } 01385 01386 QString MailCopiesTo::asUnicodeString() const 01387 { 01388 if ( !AddressList::isEmpty() ) { 01389 return AddressList::asUnicodeString(); 01390 } 01391 if ( d_func()->alwaysCopy ) { 01392 return QLatin1String( "poster" ); 01393 } 01394 if ( d_func()->neverCopy ) { 01395 return QLatin1String( "nobody" ); 01396 } 01397 return QString(); 01398 } 01399 01400 void MailCopiesTo::clear() 01401 { 01402 Q_D(MailCopiesTo); 01403 AddressList::clear(); 01404 d->alwaysCopy = false; 01405 d->neverCopy = false; 01406 } 01407 01408 bool MailCopiesTo::isEmpty() const 01409 { 01410 return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy); 01411 } 01412 01413 bool MailCopiesTo::alwaysCopy() const 01414 { 01415 return !AddressList::isEmpty() || d_func()->alwaysCopy; 01416 } 01417 01418 void MailCopiesTo::setAlwaysCopy() 01419 { 01420 Q_D(MailCopiesTo); 01421 clear(); 01422 d->alwaysCopy = true; 01423 } 01424 01425 bool MailCopiesTo::neverCopy() const 01426 { 01427 return d_func()->neverCopy; 01428 } 01429 01430 void MailCopiesTo::setNeverCopy() 01431 { 01432 Q_D(MailCopiesTo); 01433 clear(); 01434 d->neverCopy = true; 01435 } 01436 01437 bool MailCopiesTo::parse( const char *& scursor, const char * const send, 01438 bool isCRLF ) 01439 { 01440 Q_D(MailCopiesTo); 01441 clear(); 01442 if ( send - scursor == 5 ) { 01443 if ( qstrnicmp( "never", scursor, 5 ) == 0 ) { 01444 d->neverCopy = true; 01445 return true; 01446 } 01447 } 01448 if ( send - scursor == 6 ) { 01449 if ( qstrnicmp( "always", scursor, 6 ) == 0 || qstrnicmp( "poster", scursor, 6 ) == 0 ) { 01450 d->alwaysCopy = true; 01451 return true; 01452 } 01453 if ( qstrnicmp( "nobody", scursor, 6 ) == 0 ) { 01454 d->neverCopy = true; 01455 return true; 01456 } 01457 } 01458 return AddressList::parse( scursor, send, isCRLF ); 01459 } 01460 01461 //-----</MailCopiesTo>------------------------- 01462 01463 //-----<Date>---------------------------------- 01464 01465 //@cond PRIVATE 01466 kmime_mk_trivial_ctor_with_name_and_dptr( Date, Generics::Structured, Date ) 01467 //@endcond 01468 01469 QByteArray Date::as7BitString( bool withHeaderType ) const 01470 { 01471 if ( isEmpty() ) { 01472 return QByteArray(); 01473 } 01474 01475 QByteArray rv; 01476 if ( withHeaderType ) { 01477 rv += typeIntro(); 01478 } 01479 rv += d_func()->dateTime.toString( KDateTime::RFCDateDay ).toLatin1(); 01480 return rv; 01481 } 01482 01483 void Date::clear() 01484 { 01485 Q_D(Date); 01486 d->dateTime = KDateTime(); 01487 } 01488 01489 bool Date::isEmpty() const 01490 { 01491 return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid(); 01492 } 01493 01494 KDateTime Date::dateTime() const 01495 { 01496 return d_func()->dateTime; 01497 } 01498 01499 void Date::setDateTime( const KDateTime &dt ) 01500 { 01501 Q_D(Date); 01502 d->dateTime = dt; 01503 } 01504 01505 int Date::ageInDays() const 01506 { 01507 QDate today = QDate::currentDate(); 01508 return dateTime().date().daysTo(today); 01509 } 01510 01511 bool Date::parse( const char* &scursor, const char *const send, bool isCRLF ) 01512 { 01513 Q_D(Date); 01514 return parseDateTime( scursor, send, d->dateTime, isCRLF ); 01515 } 01516 01517 //-----</Date>--------------------------------- 01518 01519 //-----<Newsgroups>---------------------------- 01520 01521 //@cond PRIVATE 01522 kmime_mk_trivial_ctor_with_name_and_dptr( Newsgroups, Generics::Structured, Newsgroups ) 01523 kmime_mk_trivial_ctor_with_name( FollowUpTo, Newsgroups, Followup-To ) 01524 //@endcond 01525 01526 QByteArray Newsgroups::as7BitString( bool withHeaderType ) const 01527 { 01528 const Q_D(Newsgroups); 01529 if ( isEmpty() ) { 01530 return QByteArray(); 01531 } 01532 01533 QByteArray rv; 01534 if ( withHeaderType ) { 01535 rv += typeIntro(); 01536 } 01537 01538 for ( int i = 0; i < d->groups.count(); ++i ) { 01539 rv += d->groups[ i ]; 01540 if ( i != d->groups.count() - 1 ) { 01541 rv += ','; 01542 } 01543 } 01544 return rv; 01545 } 01546 01547 void Newsgroups::fromUnicodeString( const QString &s, const QByteArray &b ) 01548 { 01549 Q_UNUSED( b ); 01550 Q_D(Newsgroups); 01551 from7BitString( s.toUtf8() ); 01552 d->encCS = cachedCharset( "UTF-8" ); 01553 } 01554 01555 QString Newsgroups::asUnicodeString() const 01556 { 01557 return QString::fromUtf8( as7BitString( false ) ); 01558 } 01559 01560 void Newsgroups::clear() 01561 { 01562 Q_D(Newsgroups); 01563 d->groups.clear(); 01564 } 01565 01566 bool Newsgroups::isEmpty() const 01567 { 01568 return d_func()->groups.isEmpty(); 01569 } 01570 01571 QList<QByteArray> Newsgroups::groups() const 01572 { 01573 return d_func()->groups; 01574 } 01575 01576 void Newsgroups::setGroups( const QList<QByteArray> &groups ) 01577 { 01578 Q_D(Newsgroups); 01579 d->groups = groups; 01580 } 01581 01582 bool Newsgroups::isCrossposted() const 01583 { 01584 return d_func()->groups.count() >= 2; 01585 } 01586 01587 bool Newsgroups::parse( const char* &scursor, const char *const send, bool isCRLF ) 01588 { 01589 Q_D(Newsgroups); 01590 clear(); 01591 forever { 01592 eatCFWS( scursor, send, isCRLF ); 01593 if ( scursor != send && *scursor == ',' ) { 01594 ++scursor; 01595 } 01596 eatCFWS( scursor, send, isCRLF ); 01597 if ( scursor == send ) { 01598 return true; 01599 } 01600 const char *start = scursor; 01601 while ( scursor != send && !isspace( *scursor ) && *scursor != ',' ) { 01602 ++scursor; 01603 } 01604 QByteArray group( start, scursor - start ); 01605 d->groups.append( group ); 01606 } 01607 return true; 01608 } 01609 01610 //-----</Newsgroups>--------------------------- 01611 01612 //-----<Lines>--------------------------------- 01613 01614 //@cond PRIVATE 01615 kmime_mk_trivial_ctor_with_name_and_dptr( Lines, Generics::Structured, Lines ) 01616 //@endcond 01617 01618 QByteArray Lines::as7BitString( bool withHeaderType ) const 01619 { 01620 if ( isEmpty() ) { 01621 return QByteArray(); 01622 } 01623 01624 QByteArray num; 01625 num.setNum( d_func()->lines ); 01626 01627 if ( withHeaderType ) { 01628 return typeIntro() + num; 01629 } 01630 return num; 01631 } 01632 01633 QString Lines::asUnicodeString() const 01634 { 01635 if ( isEmpty() ) { 01636 return QString(); 01637 } 01638 return QString::number( d_func()->lines ); 01639 } 01640 01641 void Lines::clear() 01642 { 01643 Q_D(Lines); 01644 d->lines = -1; 01645 } 01646 01647 bool Lines::isEmpty() const 01648 { 01649 return d_func()->lines == -1; 01650 } 01651 01652 int Lines::numberOfLines() const 01653 { 01654 return d_func()->lines; 01655 } 01656 01657 void Lines::setNumberOfLines( int lines ) 01658 { 01659 Q_D(Lines); 01660 d->lines = lines; 01661 } 01662 01663 bool Lines::parse( const char* &scursor, const char* const send, bool isCRLF ) 01664 { 01665 Q_D(Lines); 01666 eatCFWS( scursor, send, isCRLF ); 01667 if ( parseDigits( scursor, send, d->lines ) == 0 ) { 01668 clear(); 01669 return false; 01670 } 01671 return true; 01672 } 01673 01674 //-----</Lines>-------------------------------- 01675 01676 //-----<Content-Type>-------------------------- 01677 01678 //@cond PRIVATE 01679 kmime_mk_trivial_ctor_with_name_and_dptr( ContentType, Generics::Parametrized, 01680 Content-Type ) 01681 //@endcond 01682 01683 bool ContentType::isEmpty() const 01684 { 01685 return d_func()->mimeType.isEmpty(); 01686 } 01687 01688 void ContentType::clear() 01689 { 01690 Q_D(ContentType); 01691 d->category = CCsingle; 01692 d->mimeType.clear(); 01693 d->mimeSubType.clear(); 01694 Parametrized::clear(); 01695 } 01696 01697 QByteArray ContentType::as7BitString( bool withHeaderType ) const 01698 { 01699 if ( isEmpty() ) { 01700 return QByteArray(); 01701 } 01702 01703 QByteArray rv; 01704 if ( withHeaderType ) { 01705 rv += typeIntro(); 01706 } 01707 01708 rv += mimeType(); 01709 if ( !Parametrized::isEmpty() ) { 01710 rv += "; " + Parametrized::as7BitString( false ); 01711 } 01712 01713 return rv; 01714 } 01715 01716 QByteArray ContentType::mimeType() const 01717 { 01718 return d_func()->mimeType + '/' + d_func()->mimeSubType; 01719 } 01720 01721 QByteArray ContentType::mediaType() const 01722 { 01723 return d_func()->mimeType; 01724 } 01725 01726 QByteArray ContentType::subType() const 01727 { 01728 return d_func()->mimeSubType; 01729 } 01730 01731 void ContentType::setMimeType( const QByteArray &mimeType ) 01732 { 01733 Q_D(ContentType); 01734 int pos = mimeType.indexOf( '/' ); 01735 if ( pos < 0 ) { 01736 d->mimeType = mimeType; 01737 d->mimeSubType.clear(); 01738 } else { 01739 d->mimeType = mimeType.left( pos ); 01740 d->mimeSubType = mimeType.mid( pos + 1 ); 01741 } 01742 Parametrized::clear(); 01743 01744 if ( isMultipart() ) { 01745 d->category = CCcontainer; 01746 } else { 01747 d->category = CCsingle; 01748 } 01749 } 01750 01751 bool ContentType::isMediatype( const char *mediatype ) const 01752 { 01753 return strncasecmp( mediaType().constData(), mediatype, strlen( mediatype ) ) == 0; 01754 } 01755 01756 bool ContentType::isSubtype( const char *subtype ) const 01757 { 01758 return strncasecmp( subType().constData(), subtype, strlen( subtype ) ) == 0; 01759 } 01760 01761 bool ContentType::isText() const 01762 { 01763 return ( strncasecmp( mediaType().constData(), "text", 4 ) == 0 01764 || isEmpty() ); 01765 } 01766 01767 bool ContentType::isPlainText() const 01768 { 01769 return ( strcasecmp( mimeType().constData(), "text/plain" ) == 0 01770 || isEmpty() ); 01771 } 01772 01773 bool ContentType::isHTMLText() const 01774 { 01775 return strcasecmp( mimeType().constData(), "text/html" ) == 0; 01776 } 01777 01778 bool ContentType::isImage() const 01779 { 01780 return strncasecmp( mediaType().constData(), "image", 5 ) == 0; 01781 } 01782 01783 bool ContentType::isMultipart() const 01784 { 01785 return strncasecmp( mediaType().constData(), "multipart", 9 ) == 0; 01786 } 01787 01788 bool ContentType::isPartial() const 01789 { 01790 return strcasecmp( mimeType().constData(), "message/partial" ) == 0; 01791 } 01792 01793 QByteArray ContentType::charset() const 01794 { 01795 QByteArray ret = parameter( "charset" ).toLatin1(); 01796 if ( ret.isEmpty() || forceDefaultCharset() ) { 01797 //return the default-charset if necessary 01798 ret = defaultCharset(); 01799 } 01800 return ret; 01801 } 01802 01803 void ContentType::setCharset( const QByteArray &s ) 01804 { 01805 setParameter( "charset", QString::fromLatin1( s ) ); 01806 } 01807 01808 QByteArray ContentType::boundary() const 01809 { 01810 return parameter( "boundary" ).toLatin1(); 01811 } 01812 01813 void ContentType::setBoundary( const QByteArray &s ) 01814 { 01815 setParameter( "boundary", QString::fromLatin1( s ) ); 01816 } 01817 01818 QString ContentType::name() const 01819 { 01820 return parameter( "name" ); 01821 } 01822 01823 void ContentType::setName( const QString &s, const QByteArray &cs ) 01824 { 01825 Q_D(ContentType); 01826 d->encCS = cs; 01827 setParameter( "name", s ); 01828 } 01829 01830 QByteArray ContentType::id() const 01831 { 01832 return parameter( "id" ).toLatin1(); 01833 } 01834 01835 void ContentType::setId( const QByteArray &s ) 01836 { 01837 setParameter( "id", s ); 01838 } 01839 01840 int ContentType::partialNumber() const 01841 { 01842 QByteArray p = parameter( "number" ).toLatin1(); 01843 if ( !p.isEmpty() ) { 01844 return p.toInt(); 01845 } else { 01846 return -1; 01847 } 01848 } 01849 01850 int ContentType::partialCount() const 01851 { 01852 QByteArray p = parameter( "total" ).toLatin1(); 01853 if ( !p.isEmpty() ) { 01854 return p.toInt(); 01855 } else { 01856 return -1; 01857 } 01858 } 01859 01860 contentCategory ContentType::category() const 01861 { 01862 return d_func()->category; 01863 } 01864 01865 void ContentType::setCategory( contentCategory c ) 01866 { 01867 Q_D(ContentType); 01868 d->category = c; 01869 } 01870 01871 void ContentType::setPartialParams( int total, int number ) 01872 { 01873 setParameter( "number", QString::number( number ) ); 01874 setParameter( "total", QString::number( total ) ); 01875 } 01876 01877 bool ContentType::parse( const char* &scursor, const char * const send, 01878 bool isCRLF ) 01879 { 01880 Q_D(ContentType); 01881 // content-type: type "/" subtype *(";" parameter) 01882 01883 clear(); 01884 eatCFWS( scursor, send, isCRLF ); 01885 if ( scursor == send ) { 01886 return false; // empty header 01887 } 01888 01889 // type 01890 QPair<const char*,int> maybeMimeType; 01891 if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) { 01892 return false; 01893 } 01894 d->mimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower(); 01895 01896 // subtype 01897 eatCFWS( scursor, send, isCRLF ); 01898 if ( scursor == send || *scursor != '/' ) { 01899 return false; 01900 } 01901 scursor++; 01902 eatCFWS( scursor, send, isCRLF ); 01903 if ( scursor == send ) { 01904 return false; 01905 } 01906 01907 QPair<const char*,int> maybeSubType; 01908 if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) { 01909 return false; 01910 } 01911 d->mimeSubType = QByteArray( maybeSubType.first, maybeSubType.second ).toLower(); 01912 01913 // parameter list 01914 eatCFWS( scursor, send, isCRLF ); 01915 if ( scursor == send ) { 01916 goto success; // no parameters 01917 } 01918 01919 if ( *scursor != ';' ) { 01920 return false; 01921 } 01922 scursor++; 01923 01924 if ( !Parametrized::parse( scursor, send, isCRLF ) ) { 01925 return false; 01926 } 01927 01928 // adjust category 01929 success: 01930 if ( isMultipart() ) { 01931 d->category = CCcontainer; 01932 } else { 01933 d->category = CCsingle; 01934 } 01935 return true; 01936 } 01937 01938 //-----</Content-Type>------------------------- 01939 01940 //-----<ContentID>---------------------- 01941 01942 kmime_mk_trivial_ctor_with_name_and_dptr( ContentID, SingleIdent, Content-ID ) 01943 kmime_mk_dptr_ctor( ContentID, SingleIdent ) 01944 01945 bool ContentID::parse( const char* &scursor, const char *const send, bool isCRLF ) 01946 { 01947 Q_D ( ContentID ); 01948 // Content-id := "<" contentid ">" 01949 // contentid := now whitespaces 01950 01951 const char* origscursor = scursor; 01952 if ( !SingleIdent::parse ( scursor, send, isCRLF ) ) 01953 { 01954 scursor = origscursor; 01955 d->msgIdList.clear(); 01956 01957 while ( scursor != send ) 01958 { 01959 eatCFWS ( scursor, send, isCRLF ); 01960 // empty entry ending the list: OK. 01961 if ( scursor == send ) 01962 { 01963 return true; 01964 } 01965 // empty entry: ignore. 01966 if ( *scursor == ',' ) 01967 { 01968 scursor++; 01969 continue; 01970 } 01971 01972 AddrSpec maybeContentId; 01973 // Almost parseAngleAddr 01974 if ( scursor == send || *scursor != '<' ) 01975 { 01976 return false; 01977 } 01978 scursor++; // eat '<' 01979 01980 eatCFWS ( scursor, send, isCRLF ); 01981 if ( scursor == send ) 01982 { 01983 return false; 01984 } 01985 01986 // Save chars untill '>'' 01987 QString result = ""; 01988 if( !parseAtom(scursor, send, result, false) ) { 01989 return false; 01990 } 01991 01992 eatCFWS ( scursor, send, isCRLF ); 01993 if ( scursor == send || *scursor != '>' ) 01994 { 01995 return false; 01996 } 01997 scursor++; 01998 // /Almost parseAngleAddr 01999 02000 maybeContentId.localPart = result; 02001 d->msgIdList.append ( maybeContentId ); 02002 02003 eatCFWS ( scursor, send, isCRLF ); 02004 // header end ending the list: OK. 02005 if ( scursor == send ) 02006 { 02007 return true; 02008 } 02009 // regular item separator: eat it. 02010 if ( *scursor == ',' ) 02011 { 02012 scursor++; 02013 } 02014 } 02015 return true; 02016 } 02017 else 02018 { 02019 return true; 02020 } 02021 } 02022 02023 //-----</ContentID>---------------------- 02024 02025 //-----<ContentTransferEncoding>---------------------------- 02026 02027 //@cond PRIVATE 02028 kmime_mk_trivial_ctor_with_name_and_dptr( ContentTransferEncoding, 02029 Generics::Token, Content-Transfer-Encoding ) 02030 //@endcond 02031 02032 typedef struct { const char *s; int e; } encTableType; 02033 02034 static const encTableType encTable[] = 02035 { 02036 { "7Bit", CE7Bit }, 02037 { "8Bit", CE8Bit }, 02038 { "quoted-printable", CEquPr }, 02039 { "base64", CEbase64 }, 02040 { "x-uuencode", CEuuenc }, 02041 { "binary", CEbinary }, 02042 { 0, 0} 02043 }; 02044 02045 void ContentTransferEncoding::clear() 02046 { 02047 Q_D(ContentTransferEncoding); 02048 d->decoded = true; 02049 d->cte = CE7Bit; 02050 Token::clear(); 02051 } 02052 02053 contentEncoding ContentTransferEncoding::encoding() const 02054 { 02055 return d_func()->cte; 02056 } 02057 02058 void ContentTransferEncoding::setEncoding( contentEncoding e ) 02059 { 02060 Q_D(ContentTransferEncoding); 02061 d->cte = e; 02062 02063 for ( int i = 0; encTable[i].s != 0; ++i ) { 02064 if ( d->cte == encTable[i].e ) { 02065 setToken( encTable[i].s ); 02066 break; 02067 } 02068 } 02069 } 02070 02071 bool ContentTransferEncoding::decoded() const 02072 { 02073 return d_func()->decoded; 02074 } 02075 02076 void ContentTransferEncoding::setDecoded( bool decoded ) 02077 { 02078 Q_D(ContentTransferEncoding); 02079 d->decoded = decoded; 02080 } 02081 02082 bool ContentTransferEncoding::needToEncode() const 02083 { 02084 const Q_D(ContentTransferEncoding); 02085 return d->decoded && (d->cte == CEquPr || d->cte == CEbase64); 02086 } 02087 02088 bool ContentTransferEncoding::parse( const char *& scursor, 02089 const char * const send, bool isCRLF ) 02090 { 02091 Q_D(ContentTransferEncoding); 02092 clear(); 02093 if ( !Token::parse( scursor, send, isCRLF ) ) { 02094 return false; 02095 } 02096 02097 // TODO: error handling in case of an unknown encoding? 02098 for ( int i = 0; encTable[i].s != 0; ++i ) { 02099 if ( strcasecmp( token().constData(), encTable[i].s ) == 0 ) { 02100 d->cte = ( contentEncoding )encTable[i].e; 02101 break; 02102 } 02103 } 02104 d->decoded = ( d->cte == CE7Bit || d->cte == CE8Bit ); 02105 return true; 02106 } 02107 02108 //-----</ContentTransferEncoding>--------------------------- 02109 02110 //-----<ContentDisposition>-------------------------- 02111 02112 //@cond PRIVATE 02113 kmime_mk_trivial_ctor_with_name_and_dptr( ContentDisposition, 02114 Generics::Parametrized, Content-Disposition ) 02115 //@endcond 02116 02117 QByteArray ContentDisposition::as7BitString( bool withHeaderType ) const 02118 { 02119 if ( isEmpty() ) { 02120 return QByteArray(); 02121 } 02122 02123 QByteArray rv; 02124 if ( withHeaderType ) { 02125 rv += typeIntro(); 02126 } 02127 02128 if ( d_func()->disposition == CDattachment ) { 02129 rv += "attachment"; 02130 } else if ( d_func()->disposition == CDinline ) { 02131 rv += "inline"; 02132 } else { 02133 return QByteArray(); 02134 } 02135 02136 if ( !Parametrized::isEmpty() ) { 02137 rv += "; " + Parametrized::as7BitString( false ); 02138 } 02139 02140 return rv; 02141 } 02142 02143 bool ContentDisposition::isEmpty() const 02144 { 02145 return d_func()->disposition == CDInvalid; 02146 } 02147 02148 void ContentDisposition::clear() 02149 { 02150 Q_D(ContentDisposition); 02151 d->disposition = CDInvalid; 02152 Parametrized::clear(); 02153 } 02154 02155 contentDisposition ContentDisposition::disposition() const 02156 { 02157 return d_func()->disposition; 02158 } 02159 02160 void ContentDisposition::setDisposition( contentDisposition disp ) 02161 { 02162 Q_D(ContentDisposition); 02163 d->disposition = disp; 02164 } 02165 02166 QString KMime::Headers::ContentDisposition::filename() const 02167 { 02168 return parameter( "filename" ); 02169 } 02170 02171 void ContentDisposition::setFilename( const QString &filename ) 02172 { 02173 setParameter( "filename", filename ); 02174 } 02175 02176 bool ContentDisposition::parse( const char *& scursor, const char * const send, 02177 bool isCRLF ) 02178 { 02179 Q_D(ContentDisposition); 02180 clear(); 02181 02182 // token 02183 QByteArray token; 02184 eatCFWS( scursor, send, isCRLF ); 02185 if ( scursor == send ) { 02186 return false; 02187 } 02188 02189 QPair<const char*,int> maybeToken; 02190 if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) { 02191 return false; 02192 } 02193 02194 token = QByteArray( maybeToken.first, maybeToken.second ).toLower(); 02195 if ( token == "inline" ) { 02196 d->disposition = CDinline; 02197 } else if ( token == "attachment" ) { 02198 d->disposition = CDattachment; 02199 } else { 02200 return false; 02201 } 02202 02203 // parameter list 02204 eatCFWS( scursor, send, isCRLF ); 02205 if ( scursor == send ) { 02206 return true; // no parameters 02207 } 02208 02209 if ( *scursor != ';' ) { 02210 return false; 02211 } 02212 scursor++; 02213 02214 return Parametrized::parse( scursor, send, isCRLF ); 02215 } 02216 02217 //-----</ContentDisposition>------------------------- 02218 02219 //@cond PRIVATE 02220 kmime_mk_trivial_ctor_with_name( Subject, Generics::Unstructured, Subject ) 02221 //@endcond 02222 02223 bool Subject::isReply() const 02224 { 02225 return asUnicodeString().indexOf( QLatin1String( "Re:" ), 0, Qt::CaseInsensitive ) == 0; 02226 } 02227 02228 //@cond PRIVATE 02229 kmime_mk_trivial_ctor_with_name( ContentDescription, 02230 Generics::Unstructured, Content-Description ) 02231 kmime_mk_trivial_ctor_with_name( ContentLocation, 02232 Generics::Unstructured, Content-Location ) 02233 kmime_mk_trivial_ctor_with_name( From, Generics::MailboxList, From ) 02234 kmime_mk_trivial_ctor_with_name( Sender, Generics::SingleMailbox, Sender ) 02235 kmime_mk_trivial_ctor_with_name( To, Generics::AddressList, To ) 02236 kmime_mk_trivial_ctor_with_name( Cc, Generics::AddressList, Cc ) 02237 kmime_mk_trivial_ctor_with_name( Bcc, Generics::AddressList, Bcc ) 02238 kmime_mk_trivial_ctor_with_name( ReplyTo, Generics::AddressList, Reply-To ) 02239 kmime_mk_trivial_ctor_with_name( Keywords, Generics::PhraseList, Keywords ) 02240 kmime_mk_trivial_ctor_with_name( MIMEVersion, Generics::DotAtom, MIME-Version ) 02241 kmime_mk_trivial_ctor_with_name( Supersedes, Generics::SingleIdent, Supersedes ) 02242 kmime_mk_trivial_ctor_with_name( InReplyTo, Generics::Ident, In-Reply-To ) 02243 kmime_mk_trivial_ctor_with_name( References, Generics::Ident, References ) 02244 kmime_mk_trivial_ctor_with_name( Organization, Generics::Unstructured, Organization ) 02245 kmime_mk_trivial_ctor_with_name( UserAgent, Generics::Unstructured, User-Agent ) 02246 //@endcond 02247 02248 } // namespace Headers 02249 02250 } // namespace KMime