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