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