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