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