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