1/* -*- c++ -*-
2 kmime_headers.cpp
3
4 KMime, the KDE Internet mail/usenet news message library.
5 Copyright (c) 2001-2002 the KMime authors.
6 See file AUTHORS for details
7 Copyright (c) 2006 Volker Krause <vkrause@kde.org>
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23*/
24/**
25 @file
26 This file is part of the API for handling @ref MIME data and
27 defines the various header classes:
28 - header's base class defining the common interface
29 - generic base classes for different types of fields
30 - incompatible, Structured-based field classes
31 - compatible, Unstructured-based field classes
32
33 @brief
34 Defines the various headers classes.
35
36 @authors the KMime authors (see AUTHORS file),
37 Volker Krause \<vkrause@kde.org\>
38*/
39
40#include "kmime_headers.h"
41#include "kmime_headers_p.h"
42
43#include "kmime_util.h"
44#include "kmime_util_p.h"
45#include "kmime_content.h"
46#include "kmime_codecs.h"
47#include "kmime_header_parsing.h"
48#include "kmime_headerfactory_p.h"
49#include "kmime_warning.h"
50
51#include <QtCore/QTextCodec>
52#include <QtCore/QString>
53#include <QtCore/QStringList>
54
55#include <kglobal.h>
56#include <kcharsets.h>
57
58#include <assert.h>
59#include <ctype.h>
60
61template <typename T>
62bool registerHeaderHelper()
63{
64 const T dummy;
65 if ( QByteArray( dummy.type() ).isEmpty() ) {
66 // This is a generic header.
67 return false;
68 }
69 return KMime::HeaderFactory::self()->registerHeader<T>();
70}
71
72// macro to register a header with HeaderFactory
73#define kmime_register_header( subclass ) \
74namespace { const bool dummyForRegistering##subclass = registerHeaderHelper<subclass>(); }
75
76// macro to generate a default constructor implementation
77#define kmime_mk_trivial_ctor( subclass, baseclass ) \
78subclass::subclass( Content *parent ) : baseclass( parent ) \
79{ \
80 clear(); \
81} \
82 \
83subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( parent ) \
84{ \
85 from7BitString( s ); \
86} \
87 \
88subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
89 baseclass( parent ) \
90{ \
91 fromUnicodeString( s, charset ); \
92} \
93 \
94subclass::~subclass() {} \
95 \
96kmime_register_header( subclass )
97// end kmime_mk_trivial_ctor
98
99
100#define kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
101subclass::subclass( Content *parent ) : baseclass( new subclass##Private, parent ) \
102{ \
103 clear(); \
104} \
105 \
106subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( new subclass##Private, parent ) \
107{ \
108 from7BitString( s ); \
109} \
110 \
111subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
112 baseclass( new subclass##Private, parent ) \
113{ \
114 fromUnicodeString( s, charset ); \
115} \
116 \
117subclass::~subclass() {} \
118 \
119kmime_register_header( subclass )
120// end kmime_mk_trivial_ctor_with_dptr
121
122
123#define kmime_mk_trivial_ctor_with_name( subclass, baseclass, name ) \
124kmime_mk_trivial_ctor( subclass, baseclass ) \
125 \
126const char *subclass::type() const \
127{ \
128 return staticType(); \
129} \
130const char *subclass::staticType() { return #name; }
131
132#define kmime_mk_trivial_ctor_with_name_and_dptr( subclass, baseclass, name ) \
133kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
134const char *subclass::type() const { return staticType(); } \
135const char *subclass::staticType() { return #name; }
136
137#define kmime_mk_dptr_ctor( subclass, baseclass ) \
138subclass::subclass( subclass##Private *d, KMime::Content *parent ) : baseclass( d, parent ) {}
139
140using namespace KMime;
141using namespace KMime::Headers;
142using namespace KMime::Types;
143using namespace KMime::HeaderParsing;
144
145namespace KMime {
146namespace Headers {
147//-----<Base>----------------------------------
148Base::Base( KMime::Content *parent ) :
149 d_ptr( new BasePrivate )
150{
151 Q_D( Base );
152 d->parent = parent;
153}
154
155Base::Base( BasePrivate *dd, KMime::Content *parent ) :
156 d_ptr( dd )
157{
158 Q_D( Base );
159 d->parent = parent;
160}
161
162Base::~Base()
163{
164 delete d_ptr;
165 d_ptr = 0;
166}
167
168KMime::Content *Base::parent() const
169{
170 return d_ptr->parent;
171}
172
173void Base::setParent( KMime::Content *parent )
174{
175 d_ptr->parent = parent;
176}
177
178QByteArray Base::rfc2047Charset() const
179{
180 if ( d_ptr->encCS.isEmpty() || forceDefaultCharset() ) {
181 return defaultCharset();
182 } else {
183 return d_ptr->encCS;
184 }
185}
186
187void Base::setRFC2047Charset( const QByteArray &cs )
188{
189 d_ptr->encCS = cachedCharset( cs );
190}
191
192bool Base::forceDefaultCharset() const
193{
194 return ( parent() != 0 ? parent()->forceDefaultCharset() : false );
195}
196
197QByteArray Base::defaultCharset() const
198{
199 return ( parent() != 0 ? parent()->defaultCharset() : Latin1 );
200}
201
202const char *Base::type() const
203{
204 return "";
205}
206
207bool Base::is( const char *t ) const
208{
209 return qstricmp( t, type() ) == 0;
210}
211
212bool Base::isMimeHeader() const
213{
214 return qstrnicmp( type(), "Content-", 8 ) == 0;
215}
216
217bool Base::isXHeader() const
218{
219 return qstrncmp( type(), "X-", 2 ) == 0;
220}
221
222QByteArray Base::typeIntro() const
223{
224 return QByteArray( type() ) + ": ";
225}
226
227//-----</Base>---------------------------------
228
229namespace Generics {
230
231//-----<Unstructured>-------------------------
232
233//@cond PRIVATE
234kmime_mk_dptr_ctor( Unstructured, Base )
235//@endcond
236
237Unstructured::Unstructured( Content *p ) : Base( new UnstructuredPrivate, p )
238{
239}
240
241Unstructured::Unstructured( Content *p, const QByteArray &s ) : Base( new UnstructuredPrivate, p )
242{
243 from7BitString( s );
244}
245
246Unstructured::Unstructured( Content *p, const QString &s, const QByteArray &cs ) : Base( new UnstructuredPrivate, p )
247{
248 fromUnicodeString( s, cs );
249}
250
251Unstructured::~Unstructured()
252{
253}
254
255void Unstructured::from7BitString( const QByteArray &s )
256{
257 Q_D( Unstructured );
258 d->decoded = decodeRFC2047String( s, d->encCS, defaultCharset(), forceDefaultCharset() );
259}
260
261QByteArray Unstructured::as7BitString( bool withHeaderType ) const
262{
263 const Q_D( Unstructured );
264 QByteArray result;
265 if ( withHeaderType ) {
266 result = typeIntro();
267 }
268 result += encodeRFC2047String( d->decoded, d->encCS ) ;
269
270 return result;
271}
272
273void Unstructured::fromUnicodeString( const QString &s, const QByteArray &b )
274{
275 Q_D( Unstructured );
276 d->decoded = s;
277 d->encCS = cachedCharset( b );
278}
279
280QString Unstructured::asUnicodeString() const
281{
282 return d_func()->decoded;
283}
284
285void Unstructured::clear()
286{
287 Q_D( Unstructured );
288 d->decoded.truncate( 0 );
289}
290
291bool Unstructured::isEmpty() const
292{
293 return d_func()->decoded.isEmpty();
294}
295
296//-----</Unstructured>-------------------------
297
298//-----<Structured>-------------------------
299
300Structured::Structured( Content *p ) : Base( new StructuredPrivate, p )
301{
302}
303
304Structured::Structured( Content *p, const QByteArray &s ) : Base( new StructuredPrivate, p )
305{
306 from7BitString( s );
307}
308
309Structured::Structured( Content *p, const QString &s, const QByteArray &cs ) : Base( new StructuredPrivate, p )
310{
311 fromUnicodeString( s, cs );
312}
313
314kmime_mk_dptr_ctor( Structured, Base )
315
316Structured::~Structured()
317{
318}
319
320void Structured::from7BitString( const QByteArray &s )
321{
322 Q_D( Structured );
323 if ( d->encCS.isEmpty() ) {
324 d->encCS = defaultCharset();
325 }
326 const char *cursor = s.constData();
327 parse( cursor, cursor + s.length() );
328}
329
330QString Structured::asUnicodeString() const
331{
332 return QString::fromLatin1( as7BitString( false ) );
333}
334
335void Structured::fromUnicodeString( const QString &s, const QByteArray &b )
336{
337 Q_D( Structured );
338 d->encCS = cachedCharset( b );
339 from7BitString( s.toLatin1() );
340}
341
342//-----</Structured>-------------------------
343
344//-----<Address>-------------------------
345
346Address::Address( Content *p ) : Structured( new AddressPrivate, p )
347{
348}
349
350Address::Address( Content *p, const QByteArray &s ) : Structured( new AddressPrivate, p )
351{
352 from7BitString( s );
353}
354
355Address::Address( Content *p, const QString &s, const QByteArray &cs ) : Structured( new AddressPrivate, p )
356{
357 fromUnicodeString( s, cs );
358}
359
360kmime_mk_dptr_ctor( Address, Structured )
361
362Address:: ~Address()
363{
364}
365
366// helper method used in AddressList and MailboxList
367static bool stringToMailbox( const QByteArray &address,
368 const QString &displayName, Types::Mailbox &mbox )
369{
370 Types::AddrSpec addrSpec;
371 mbox.setName( displayName );
372 const char *cursor = address.constData();
373 if ( !parseAngleAddr( cursor, cursor + address.length(), addrSpec ) ) {
374 if ( !parseAddrSpec( cursor, cursor + address.length(), addrSpec ) ) {
375 kWarning() << "Invalid address";
376 return false;
377 }
378 }
379 mbox.setAddress( addrSpec );
380 return true;
381}
382
383//-----</Address>-------------------------
384
385//-----<MailboxList>-------------------------
386
387kmime_mk_trivial_ctor_with_dptr( MailboxList, Address )
388kmime_mk_dptr_ctor( MailboxList, Address )
389
390QByteArray MailboxList::as7BitString( bool withHeaderType ) const
391{
392 const Q_D( MailboxList );
393 if ( isEmpty() ) {
394 return QByteArray();
395 }
396
397 QByteArray rv;
398 if ( withHeaderType ) {
399 rv = typeIntro();
400 }
401 foreach ( const Types::Mailbox &mbox, d->mailboxList ) {
402 rv += mbox.as7BitString( d->encCS );
403 rv += ", ";
404 }
405 rv.resize( rv.length() - 2 );
406 return rv;
407}
408
409void MailboxList::fromUnicodeString( const QString &s, const QByteArray &b )
410{
411 Q_D( MailboxList );
412 d->encCS = cachedCharset( b );
413 from7BitString( encodeRFC2047Sentence( s, b ) );
414}
415
416QString MailboxList::asUnicodeString() const
417{
418 return prettyAddresses().join( QLatin1String( ", " ) );
419}
420
421void MailboxList::clear()
422{
423 Q_D( MailboxList );
424 d->mailboxList.clear();
425}
426
427bool MailboxList::isEmpty() const
428{
429 return d_func()->mailboxList.isEmpty();
430}
431
432void MailboxList::addAddress( const Types::Mailbox &mbox )
433{
434 Q_D( MailboxList );
435 d->mailboxList.append( mbox );
436}
437
438void MailboxList::addAddress( const QByteArray &address,
439 const QString &displayName )
440{
441 Q_D( MailboxList );
442 Types::Mailbox mbox;
443 if ( stringToMailbox( address, displayName, mbox ) ) {
444 d->mailboxList.append( mbox );
445 }
446}
447
448QList< QByteArray > MailboxList::addresses() const
449{
450 QList<QByteArray> rv;
451 foreach ( const Types::Mailbox &mbox, d_func()->mailboxList ) {
452 rv.append( mbox.address() );
453 }
454 return rv;
455}
456
457QStringList MailboxList::displayNames() const
458{
459 QStringList rv;
460 foreach ( const Types::Mailbox &mbox, d_func()->mailboxList ) {
461 rv.append( mbox.name() );
462 }
463 return rv;
464}
465
466QStringList MailboxList::prettyAddresses() const
467{
468 QStringList rv;
469 foreach ( const Types::Mailbox &mbox, d_func()->mailboxList ) {
470 rv.append( mbox.prettyAddress() );
471 }
472 return rv;
473}
474
475Types::Mailbox::List MailboxList::mailboxes() const
476{
477 return d_func()->mailboxList;
478}
479
480bool MailboxList::parse( const char* &scursor, const char *const send,
481 bool isCRLF )
482{
483 Q_D( MailboxList );
484 // examples:
485 // from := "From:" mailbox-list CRLF
486 // sender := "Sender:" mailbox CRLF
487
488 // parse an address-list:
489 QList<Types::Address> maybeAddressList;
490 if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) {
491 return false;
492 }
493
494 d->mailboxList.clear();
495
496 // extract the mailboxes and complain if there are groups:
497 QList<Types::Address>::Iterator it;
498 for ( it = maybeAddressList.begin(); it != maybeAddressList.end() ; ++it ) {
499 if ( !( *it ).displayName.isEmpty() ) {
500 KMIME_WARN << "mailbox groups in header disallowing them! Name: \""
501 << ( *it ).displayName << "\"" << endl;
502 }
503 d->mailboxList += ( *it ).mailboxList;
504 }
505 return true;
506}
507
508//-----</MailboxList>-------------------------
509
510//-----<SingleMailbox>-------------------------
511
512//@cond PRIVATE
513kmime_mk_trivial_ctor_with_dptr( SingleMailbox, MailboxList )
514//@endcond
515
516bool SingleMailbox::parse( const char* &scursor, const char *const send,
517 bool isCRLF )
518{
519 Q_D( MailboxList );
520 if ( !MailboxList::parse( scursor, send, isCRLF ) ) {
521 return false;
522 }
523
524 if ( d->mailboxList.count() > 1 ) {
525 KMIME_WARN << "multiple mailboxes in header allowing only a single one!"
526 << endl;
527 }
528 return true;
529}
530
531//-----</SingleMailbox>-------------------------
532
533//-----<AddressList>-------------------------
534
535//@cond PRIVATE
536kmime_mk_trivial_ctor_with_dptr( AddressList, Address )
537kmime_mk_dptr_ctor( AddressList, Address )
538//@endcond
539
540QByteArray AddressList::as7BitString( bool withHeaderType ) const
541{
542 const Q_D( AddressList );
543 if ( d->addressList.isEmpty() ) {
544 return QByteArray();
545 }
546
547 QByteArray rv;
548 if ( withHeaderType ) {
549 rv = typeIntro();
550 }
551 foreach ( const Types::Address &addr, d->addressList ) {
552 foreach ( const Types::Mailbox &mbox, addr.mailboxList ) {
553 rv += mbox.as7BitString( d->encCS );
554 rv += ", ";
555 }
556 }
557 rv.resize( rv.length() - 2 );
558 return rv;
559}
560
561void AddressList::fromUnicodeString( const QString &s, const QByteArray &b )
562{
563 Q_D( AddressList );
564 d->encCS = cachedCharset( b );
565 from7BitString( encodeRFC2047Sentence( s, b ) );
566}
567
568QString AddressList::asUnicodeString() const
569{
570 return prettyAddresses().join( QLatin1String( ", " ) );
571}
572
573void AddressList::clear()
574{
575 Q_D( AddressList );
576 d->addressList.clear();
577}
578
579bool AddressList::isEmpty() const
580{
581 return d_func()->addressList.isEmpty();
582}
583
584void AddressList::addAddress( const Types::Mailbox &mbox )
585{
586 Q_D( AddressList );
587 Types::Address addr;
588 addr.mailboxList.append( mbox );
589 d->addressList.append( addr );
590}
591
592void AddressList::addAddress( const QByteArray &address,
593 const QString &displayName )
594{
595 Q_D( AddressList );
596 Types::Address addr;
597 Types::Mailbox mbox;
598 if ( stringToMailbox( address, displayName, mbox ) ) {
599 addr.mailboxList.append( mbox );
600 d->addressList.append( addr );
601 }
602}
603
604QList< QByteArray > AddressList::addresses() const
605{
606 QList<QByteArray> rv;
607 foreach ( const Types::Address &addr, d_func()->addressList ) {
608 foreach ( const Types::Mailbox &mbox, addr.mailboxList ) {
609 rv.append( mbox.address() );
610 }
611 }
612 return rv;
613}
614
615QStringList AddressList::displayNames() const
616{
617 QStringList rv;
618 foreach ( const Types::Address &addr, d_func()->addressList ) {
619 foreach ( const Types::Mailbox &mbox, addr.mailboxList ) {
620 rv.append( mbox.name() );
621 }
622 }
623 return rv;
624}
625
626QStringList AddressList::prettyAddresses() const
627{
628 QStringList rv;
629 foreach ( const Types::Address &addr, d_func()->addressList ) {
630 foreach ( const Types::Mailbox &mbox, addr.mailboxList ) {
631 rv.append( mbox.prettyAddress() );
632 }
633 }
634 return rv;
635}
636
637Types::Mailbox::List AddressList::mailboxes() const
638{
639 Types::Mailbox::List rv;
640 foreach ( const Types::Address &addr, d_func()->addressList ) {
641 foreach ( const Types::Mailbox &mbox, addr.mailboxList ) {
642 rv.append( mbox );
643 }
644 }
645 return rv;
646}
647
648bool AddressList::parse( const char* &scursor, const char *const send,
649 bool isCRLF )
650{
651 Q_D( AddressList );
652 QList<Types::Address> maybeAddressList;
653 if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) {
654 return false;
655 }
656
657 d->addressList = maybeAddressList;
658 return true;
659}
660
661//-----</AddressList>-------------------------
662
663//-----<Token>-------------------------
664
665//@cond PRIVATE
666kmime_mk_trivial_ctor_with_dptr( Token, Structured )
667kmime_mk_dptr_ctor( Token, Structured )
668//@endcond
669
670QByteArray Token::as7BitString( bool withHeaderType ) const
671{
672 if ( isEmpty() ) {
673 return QByteArray();
674 }
675 if ( withHeaderType ) {
676 return typeIntro() + d_func()->token;
677 }
678 return d_func()->token;
679}
680
681void Token::clear()
682{
683 Q_D( Token );
684 d->token.clear();
685}
686
687bool Token::isEmpty() const
688{
689 return d_func()->token.isEmpty();
690}
691
692QByteArray Token::token() const
693{
694 return d_func()->token;
695}
696
697void Token::setToken( const QByteArray &t )
698{
699 Q_D( Token );
700 d->token = t;
701}
702
703bool Token::parse( const char* &scursor, const char *const send, bool isCRLF )
704{
705 Q_D( Token );
706 clear();
707 eatCFWS( scursor, send, isCRLF );
708 // must not be empty:
709 if ( scursor == send ) {
710 return false;
711 }
712
713 QPair<const char*, int> maybeToken;
714 if ( !parseToken( scursor, send, maybeToken, false /* no 8bit chars */ ) ) {
715 return false;
716 }
717 d->token = QByteArray( maybeToken.first, maybeToken.second );
718
719 // complain if trailing garbage is found:
720 eatCFWS( scursor, send, isCRLF );
721 if ( scursor != send ) {
722 KMIME_WARN << "trailing garbage after token in header allowing "
723 "only a single token!" << endl;
724 }
725 return true;
726}
727
728//-----</Token>-------------------------
729
730//-----<PhraseList>-------------------------
731
732//@cond PRIVATE
733kmime_mk_trivial_ctor_with_dptr( PhraseList, Structured )
734//@endcond
735
736QByteArray PhraseList::as7BitString( bool withHeaderType ) const
737{
738 const Q_D( PhraseList );
739 if ( isEmpty() ) {
740 return QByteArray();
741 }
742
743 QByteArray rv;
744 if ( withHeaderType ) {
745 rv = typeIntro();
746 }
747
748 for ( int i = 0; i < d->phraseList.count(); ++i ) {
749 // FIXME: only encode when needed, quote when needed, etc.
750 rv += encodeRFC2047String( d->phraseList[i], d->encCS, false, false );
751 if ( i != d->phraseList.count() - 1 ) {
752 rv += ", ";
753 }
754 }
755
756 return rv;
757}
758
759QString PhraseList::asUnicodeString() const
760{
761 return d_func()->phraseList.join( QLatin1String( ", " ) );
762}
763
764void PhraseList::clear()
765{
766 Q_D( PhraseList );
767 d->phraseList.clear();
768}
769
770bool PhraseList::isEmpty() const
771{
772 return d_func()->phraseList.isEmpty();
773}
774
775QStringList PhraseList::phrases() const
776{
777 return d_func()->phraseList;
778}
779
780bool PhraseList::parse( const char* &scursor, const char *const send,
781 bool isCRLF )
782{
783 Q_D( PhraseList );
784 d->phraseList.clear();
785
786 while ( scursor != send ) {
787 eatCFWS( scursor, send, isCRLF );
788 // empty entry ending the list: OK.
789 if ( scursor == send ) {
790 return true;
791 }
792 // empty entry: ignore.
793 if ( *scursor == ',' ) {
794 scursor++;
795 continue;
796 }
797
798 QString maybePhrase;
799 if ( !parsePhrase( scursor, send, maybePhrase, isCRLF ) ) {
800 return false;
801 }
802 d->phraseList.append( maybePhrase );
803
804 eatCFWS( scursor, send, isCRLF );
805 // non-empty entry ending the list: OK.
806 if ( scursor == send ) {
807 return true;
808 }
809 // comma separating the phrases: eat.
810 if ( *scursor == ',' ) {
811 scursor++;
812 }
813 }
814 return true;
815}
816
817//-----</PhraseList>-------------------------
818
819//-----<DotAtom>-------------------------
820
821//@cond PRIVATE
822kmime_mk_trivial_ctor_with_dptr( DotAtom, Structured )
823//@endcond
824
825QByteArray DotAtom::as7BitString( bool withHeaderType ) const
826{
827 if ( isEmpty() ) {
828 return QByteArray();
829 }
830
831 QByteArray rv;
832 if ( withHeaderType ) {
833 rv += typeIntro();
834 }
835
836 rv += d_func()->dotAtom.toLatin1(); // FIXME: encoding?
837 return rv;
838}
839
840QString DotAtom::asUnicodeString() const
841{
842 return d_func()->dotAtom;
843}
844
845void DotAtom::clear()
846{
847 Q_D( DotAtom );
848 d->dotAtom.clear();
849}
850
851bool DotAtom::isEmpty() const
852{
853 return d_func()->dotAtom.isEmpty();
854}
855
856bool DotAtom::parse( const char* &scursor, const char *const send,
857 bool isCRLF )
858{
859 Q_D( DotAtom );
860 QString maybeDotAtom;
861 if ( !parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) {
862 return false;
863 }
864
865 d->dotAtom = maybeDotAtom;
866
867 eatCFWS( scursor, send, isCRLF );
868 if ( scursor != send ) {
869 KMIME_WARN << "trailing garbage after dot-atom in header allowing "
870 "only a single dot-atom!" << endl;
871 }
872 return true;
873}
874
875//-----</DotAtom>-------------------------
876
877//-----<Parametrized>-------------------------
878
879//@cond PRIVATE
880kmime_mk_trivial_ctor_with_dptr( Parametrized, Structured )
881kmime_mk_dptr_ctor( Parametrized, Structured )
882//@endcond
883
884QByteArray Parametrized::as7BitString( bool withHeaderType ) const
885{
886 const Q_D( Parametrized );
887 if ( isEmpty() ) {
888 return QByteArray();
889 }
890
891 QByteArray rv;
892 if ( withHeaderType ) {
893 rv += typeIntro();
894 }
895
896 bool first = true;
897 for ( QMap<QString, QString>::ConstIterator it = d->parameterHash.constBegin();
898 it != d->parameterHash.constEnd(); ++it ) {
899 if ( !first ) {
900 rv += "; ";
901 } else {
902 first = false;
903 }
904 if ( isUsAscii( it.value() ) ) {
905 rv += it.key().toLatin1() + '=';
906 QByteArray tmp = it.value().toLatin1();
907 addQuotes( tmp, true ); // force quoting, eg. for whitespaces in parameter value
908 rv += tmp;
909 } else {
910 if ( useOutlookAttachmentEncoding() ) {
911 rv += it.key().toLatin1() + '=';
912 kDebug() << "doing:" << it.value() << QLatin1String( d->encCS );
913 rv += "\"" + encodeRFC2047String( it.value(), d->encCS ) + "\"";
914 } else {
915 rv += it.key().toLatin1() + "*=";
916 rv += encodeRFC2231String( it.value(), d->encCS );
917 }
918 }
919 }
920
921 return rv;
922}
923
924QString Parametrized::parameter( const QString &key ) const
925{
926 return d_func()->parameterHash.value( key.toLower() );
927}
928
929bool Parametrized::hasParameter( const QString &key ) const
930{
931 return d_func()->parameterHash.contains( key.toLower() );
932}
933
934void Parametrized::setParameter( const QString &key, const QString &value )
935{
936 Q_D( Parametrized );
937 d->parameterHash.insert( key.toLower(), value );
938}
939
940bool Parametrized::isEmpty() const
941{
942 return d_func()->parameterHash.isEmpty();
943}
944
945void Parametrized::clear()
946{
947 Q_D( Parametrized );
948 d->parameterHash.clear();
949}
950
951bool Parametrized::parse( const char *& scursor, const char * const send,
952 bool isCRLF )
953{
954 Q_D( Parametrized );
955 d->parameterHash.clear();
956 QByteArray charset;
957 if ( !parseParameterListWithCharset( scursor, send, d->parameterHash, charset, isCRLF ) ) {
958 return false;
959 }
960 d->encCS = charset;
961 return true;
962}
963
964//-----</Parametrized>-------------------------
965
966//-----<Ident>-------------------------
967
968//@cond PRIVATE
969kmime_mk_trivial_ctor_with_dptr( Ident, Address )
970kmime_mk_dptr_ctor( Ident, Address )
971//@endcond
972
973QByteArray Ident::as7BitString( bool withHeaderType ) const
974{
975 const Q_D( Ident );
976 if ( d->msgIdList.isEmpty() ) {
977 return QByteArray();
978 }
979
980 QByteArray rv;
981 if ( withHeaderType ) {
982 rv = typeIntro();
983 }
984 foreach ( const Types::AddrSpec &addr, d->msgIdList ) {
985 if ( !addr.isEmpty() ) {
986 const QString asString = addr.asString();
987 rv += '<';
988 if ( !asString.isEmpty() ) {
989 rv += asString.toLatin1(); // FIXME: change parsing to use QByteArrays
990 }
991 rv += "> ";
992 }
993 }
994 if ( !rv.isEmpty() ) {
995 rv.resize( rv.length() - 1 );
996 }
997 return rv;
998}
999
1000void Ident::clear()
1001{
1002 Q_D( Ident );
1003 d->msgIdList.clear();
1004 d->cachedIdentifier.clear();
1005}
1006
1007bool Ident::isEmpty() const
1008{
1009 return d_func()->msgIdList.isEmpty();
1010}
1011
1012bool Ident::parse( const char* &scursor, const char * const send, bool isCRLF )
1013{
1014 Q_D( Ident );
1015 // msg-id := "<" id-left "@" id-right ">"
1016 // id-left := dot-atom-text / no-fold-quote / local-part
1017 // id-right := dot-atom-text / no-fold-literal / domain
1018 //
1019 // equivalent to:
1020 // msg-id := angle-addr
1021
1022 d->msgIdList.clear();
1023 d->cachedIdentifier.clear();
1024
1025 while ( scursor != send ) {
1026 eatCFWS( scursor, send, isCRLF );
1027 // empty entry ending the list: OK.
1028 if ( scursor == send ) {
1029 return true;
1030 }
1031 // empty entry: ignore.
1032 if ( *scursor == ',' ) {
1033 scursor++;
1034 continue;
1035 }
1036
1037 AddrSpec maybeMsgId;
1038 if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) {
1039 return false;
1040 }
1041 d->msgIdList.append( maybeMsgId );
1042
1043 eatCFWS( scursor, send, isCRLF );
1044 // header end ending the list: OK.
1045 if ( scursor == send ) {
1046 return true;
1047 }
1048 // regular item separator: eat it.
1049 if ( *scursor == ',' ) {
1050 scursor++;
1051 }
1052 }
1053 return true;
1054}
1055
1056QList<QByteArray> Ident::identifiers() const
1057{
1058 QList<QByteArray> rv;
1059 foreach ( const Types::AddrSpec &addr, d_func()->msgIdList ) {
1060 if ( !addr.isEmpty() ) {
1061 const QString asString = addr.asString();
1062 if ( !asString.isEmpty() ) {
1063 rv.append( asString.toLatin1() ); // FIXME: change parsing to use QByteArrays
1064 }
1065 }
1066 }
1067 return rv;
1068}
1069
1070void Ident::appendIdentifier( const QByteArray &id )
1071{
1072 Q_D( Ident );
1073 QByteArray tmp = id;
1074 if ( !tmp.startsWith( '<' ) ) {
1075 tmp.prepend( '<' );
1076 }
1077 if ( !tmp.endsWith( '>' ) ) {
1078 tmp.append( '>' );
1079 }
1080 AddrSpec msgId;
1081 const char *cursor = tmp.constData();
1082 if ( parseAngleAddr( cursor, cursor + tmp.length(), msgId ) ) {
1083 d->msgIdList.append( msgId );
1084 } else {
1085 kWarning() << "Unable to parse address spec!";
1086 }
1087}
1088
1089//-----</Ident>-------------------------
1090
1091//-----<SingleIdent>-------------------------
1092
1093//@cond PRIVATE
1094kmime_mk_trivial_ctor_with_dptr( SingleIdent, Ident )
1095kmime_mk_dptr_ctor( SingleIdent, Ident )
1096//@endcond
1097
1098QByteArray SingleIdent::identifier() const
1099{
1100 if ( d_func()->msgIdList.isEmpty() ) {
1101 return QByteArray();
1102 }
1103
1104 if ( d_func()->cachedIdentifier.isEmpty() ) {
1105 const Types::AddrSpec &addr = d_func()->msgIdList.first();
1106 if ( !addr.isEmpty() ) {
1107 const QString asString = addr.asString();
1108 if ( !asString.isEmpty() ) {
1109 d_func()->cachedIdentifier = asString.toLatin1();// FIXME: change parsing to use QByteArrays
1110 }
1111 }
1112 }
1113
1114 return d_func()->cachedIdentifier;
1115}
1116
1117void SingleIdent::setIdentifier( const QByteArray &id )
1118{
1119 Q_D( SingleIdent );
1120 d->msgIdList.clear();
1121 d->cachedIdentifier.clear();
1122 appendIdentifier( id );
1123}
1124
1125bool SingleIdent::parse( const char* &scursor, const char * const send,
1126 bool isCRLF )
1127{
1128 Q_D( SingleIdent );
1129 if ( !Ident::parse( scursor, send, isCRLF ) ) {
1130 return false;
1131 }
1132
1133 if ( d->msgIdList.count() > 1 ) {
1134 KMIME_WARN << "more than one msg-id in header "
1135 << "allowing only a single one!" << endl;
1136 }
1137 return true;
1138}
1139
1140//-----</SingleIdent>-------------------------
1141
1142} // namespace Generics
1143
1144//-----<ReturnPath>-------------------------
1145
1146//@cond PRIVATE
1147kmime_mk_trivial_ctor_with_name_and_dptr( ReturnPath, Generics::Address, Return-Path )
1148//@endcond
1149
1150QByteArray ReturnPath::as7BitString( bool withHeaderType ) const
1151{
1152 if ( isEmpty() ) {
1153 return QByteArray();
1154 }
1155
1156 QByteArray rv;
1157 if ( withHeaderType ) {
1158 rv += typeIntro();
1159 }
1160 rv += '<' + d_func()->mailbox.as7BitString( d_func()->encCS ) + '>';
1161 return rv;
1162}
1163
1164void ReturnPath::clear()
1165{
1166 Q_D( ReturnPath );
1167 d->mailbox.setAddress( Types::AddrSpec() );
1168 d->mailbox.setName( QString() );
1169}
1170
1171bool ReturnPath::isEmpty() const
1172{
1173 const Q_D( ReturnPath );
1174 return !d->mailbox.hasAddress() && !d->mailbox.hasName();
1175}
1176
1177bool ReturnPath::parse( const char* &scursor, const char * const send,
1178 bool isCRLF )
1179{
1180 Q_D( ReturnPath );
1181 eatCFWS( scursor, send, isCRLF );
1182 if ( scursor == send ) {
1183 return false;
1184 }
1185
1186 const char * oldscursor = scursor;
1187
1188 Mailbox maybeMailbox;
1189 if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
1190 // mailbox parsing failed, but check for empty brackets:
1191 scursor = oldscursor;
1192 if ( *scursor != '<' ) {
1193 return false;
1194 }
1195 scursor++;
1196 eatCFWS( scursor, send, isCRLF );
1197 if ( scursor == send || *scursor != '>' ) {
1198 return false;
1199 }
1200 scursor++;
1201
1202 // prepare a Null mailbox:
1203 AddrSpec emptyAddrSpec;
1204 maybeMailbox.setName( QString() );
1205 maybeMailbox.setAddress( emptyAddrSpec );
1206 } else {
1207 // check that there was no display-name:
1208 if ( maybeMailbox.hasName() ) {
1209 KMIME_WARN << "display-name \"" << maybeMailbox.name()
1210 << "\" in Return-Path!" << endl;
1211 }
1212 }
1213 d->mailbox = maybeMailbox;
1214
1215 // see if that was all:
1216 eatCFWS( scursor, send, isCRLF );
1217 // and warn if it wasn't:
1218 if ( scursor != send ) {
1219 KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl;
1220 }
1221 return true;
1222}
1223
1224//-----</ReturnPath>-------------------------
1225
1226//-----<Generic>-------------------------------
1227
1228// NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable.
1229
1230Generic::Generic() : Generics::Unstructured( new GenericPrivate )
1231{
1232}
1233
1234Generic::Generic( const char *t ) : Generics::Unstructured( new GenericPrivate )
1235{
1236 setType( t );
1237}
1238
1239Generic::Generic( const char *t, Content *p )
1240 : Generics::Unstructured( new GenericPrivate, p )
1241{
1242 setType( t );
1243}
1244
1245Generic::Generic( const char *t, Content *p, const QByteArray &s )
1246 : Generics::Unstructured( new GenericPrivate, p )
1247{
1248 from7BitString( s );
1249 setType( t );
1250}
1251
1252Generic::Generic( const char *t, Content *p, const QString &s, const QByteArray &cs )
1253 : Generics::Unstructured( new GenericPrivate, p )
1254{
1255 fromUnicodeString( s, cs );
1256 setType( t );
1257}
1258
1259Generic::~Generic()
1260{
1261}
1262
1263void Generic::clear()
1264{
1265 Q_D( Generic );
1266 delete[] d->type;
1267 d->type = 0;
1268 Unstructured::clear();
1269}
1270
1271bool Generic::isEmpty() const
1272{
1273 return d_func()->type == 0 || Unstructured::isEmpty();
1274}
1275
1276const char *Generic::type() const
1277{
1278 return d_func()->type;
1279}
1280
1281void Generic::setType( const char *type )
1282{
1283 Q_D( Generic );
1284 if ( d->type ) {
1285 delete[] d->type;
1286 }
1287 if ( type ) {
1288 d->type = new char[strlen( type )+1];
1289 strcpy( d->type, type );
1290 } else {
1291 d->type = 0;
1292 }
1293}
1294
1295//-----<Generic>-------------------------------
1296
1297//-----<MessageID>-----------------------------
1298
1299//@cond PRIVATE
1300kmime_mk_trivial_ctor_with_name( MessageID, Generics::SingleIdent, Message-ID )
1301//@endcond
1302
1303void MessageID::generate( const QByteArray &fqdn )
1304{
1305 setIdentifier( '<' + uniqueString() + '@' + fqdn + '>' );
1306}
1307
1308//-----</MessageID>----------------------------
1309
1310//-----<Control>-------------------------------
1311
1312//@cond PRIVATE
1313kmime_mk_trivial_ctor_with_name_and_dptr( Control, Generics::Structured, Control )
1314//@endcond
1315
1316QByteArray Control::as7BitString( bool withHeaderType ) const
1317{
1318 const Q_D( Control );
1319 if ( isEmpty() ) {
1320 return QByteArray();
1321 }
1322
1323 QByteArray rv;
1324 if ( withHeaderType ) {
1325 rv += typeIntro();
1326 }
1327
1328 rv += d->name;
1329 if ( !d->parameter.isEmpty() ) {
1330 rv += ' ' + d->parameter;
1331 }
1332 return rv;
1333}
1334
1335void Control::clear()
1336{
1337 Q_D( Control );
1338 d->name.clear();
1339 d->parameter.clear();
1340}
1341
1342bool Control::isEmpty() const
1343{
1344 return d_func()->name.isEmpty();
1345}
1346
1347QByteArray Control::controlType() const
1348{
1349 return d_func()->name;
1350}
1351
1352QByteArray Control::parameter() const
1353{
1354 return d_func()->parameter;
1355}
1356
1357bool Control::isCancel() const
1358{
1359 return d_func()->name.toLower() == "cancel";
1360}
1361
1362void Control::setCancel( const QByteArray &msgid )
1363{
1364 Q_D( Control );
1365 d->name = "cancel";
1366 d->parameter = msgid;
1367}
1368
1369bool Control::parse( const char* &scursor, const char *const send, bool isCRLF )
1370{
1371 Q_D( Control );
1372 clear();
1373 eatCFWS( scursor, send, isCRLF );
1374 if ( scursor == send ) {
1375 return false;
1376 }
1377 const char *start = scursor;
1378 while ( scursor != send && !isspace( *scursor ) ) {
1379 ++scursor;
1380 }
1381 d->name = QByteArray( start, scursor - start );
1382 eatCFWS( scursor, send, isCRLF );
1383 d->parameter = QByteArray( scursor, send - scursor );
1384 return true;
1385}
1386
1387//-----</Control>------------------------------
1388
1389//-----<MailCopiesTo>--------------------------
1390
1391//@cond PRIVATE
1392kmime_mk_trivial_ctor_with_name_and_dptr( MailCopiesTo,
1393 Generics::AddressList, Mail-Copies-To )
1394//@endcond
1395
1396QByteArray MailCopiesTo::as7BitString( bool withHeaderType ) const
1397{
1398 QByteArray rv;
1399 if ( withHeaderType ) {
1400 rv += typeIntro();
1401 }
1402 if ( !AddressList::isEmpty() ) {
1403 rv += AddressList::as7BitString( false );
1404 } else {
1405 if ( d_func()->alwaysCopy ) {
1406 rv += "poster";
1407 } else if ( d_func()->neverCopy ) {
1408 rv += "nobody";
1409 }
1410 }
1411 return rv;
1412}
1413
1414QString MailCopiesTo::asUnicodeString() const
1415{
1416 if ( !AddressList::isEmpty() ) {
1417 return AddressList::asUnicodeString();
1418 }
1419 if ( d_func()->alwaysCopy ) {
1420 return QLatin1String( "poster" );
1421 }
1422 if ( d_func()->neverCopy ) {
1423 return QLatin1String( "nobody" );
1424 }
1425 return QString();
1426}
1427
1428void MailCopiesTo::clear()
1429{
1430 Q_D( MailCopiesTo );
1431 AddressList::clear();
1432 d->alwaysCopy = false;
1433 d->neverCopy = false;
1434}
1435
1436bool MailCopiesTo::isEmpty() const
1437{
1438 return AddressList::isEmpty() && !( d_func()->alwaysCopy || d_func()->neverCopy );
1439}
1440
1441bool MailCopiesTo::alwaysCopy() const
1442{
1443 return !AddressList::isEmpty() || d_func()->alwaysCopy;
1444}
1445
1446void MailCopiesTo::setAlwaysCopy()
1447{
1448 Q_D( MailCopiesTo );
1449 clear();
1450 d->alwaysCopy = true;
1451}
1452
1453bool MailCopiesTo::neverCopy() const
1454{
1455 return d_func()->neverCopy;
1456}
1457
1458void MailCopiesTo::setNeverCopy()
1459{
1460 Q_D( MailCopiesTo );
1461 clear();
1462 d->neverCopy = true;
1463}
1464
1465bool MailCopiesTo::parse( const char *& scursor, const char * const send,
1466 bool isCRLF )
1467{
1468 Q_D( MailCopiesTo );
1469 clear();
1470 if ( send - scursor == 5 ) {
1471 if ( qstrnicmp( "never", scursor, 5 ) == 0 ) {
1472 d->neverCopy = true;
1473 return true;
1474 }
1475 }
1476 if ( send - scursor == 6 ) {
1477 if ( qstrnicmp( "always", scursor, 6 ) == 0 || qstrnicmp( "poster", scursor, 6 ) == 0 ) {
1478 d->alwaysCopy = true;
1479 return true;
1480 }
1481 if ( qstrnicmp( "nobody", scursor, 6 ) == 0 ) {
1482 d->neverCopy = true;
1483 return true;
1484 }
1485 }
1486 return AddressList::parse( scursor, send, isCRLF );
1487}
1488
1489//-----</MailCopiesTo>-------------------------
1490
1491//-----<Date>----------------------------------
1492
1493//@cond PRIVATE
1494kmime_mk_trivial_ctor_with_name_and_dptr( Date, Generics::Structured, Date )
1495//@endcond
1496
1497QByteArray Date::as7BitString( bool withHeaderType ) const
1498{
1499 if ( isEmpty() ) {
1500 return QByteArray();
1501 }
1502
1503 QByteArray rv;
1504 if ( withHeaderType ) {
1505 rv += typeIntro();
1506 }
1507 rv += d_func()->dateTime.toString( KDateTime::RFCDateDay ).toLatin1();
1508 return rv;
1509}
1510
1511void Date::clear()
1512{
1513 Q_D( Date );
1514 d->dateTime = KDateTime();
1515}
1516
1517bool Date::isEmpty() const
1518{
1519 return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid();
1520}
1521
1522KDateTime Date::dateTime() const
1523{
1524 return d_func()->dateTime;
1525}
1526
1527void Date::setDateTime( const KDateTime &dt )
1528{
1529 Q_D( Date );
1530 d->dateTime = dt;
1531}
1532
1533int Date::ageInDays() const
1534{
1535 QDate today = QDate::currentDate();
1536 return dateTime().date().daysTo( today );
1537}
1538
1539bool Date::parse( const char* &scursor, const char *const send, bool isCRLF )
1540{
1541 Q_D( Date );
1542 return parseDateTime( scursor, send, d->dateTime, isCRLF );
1543}
1544
1545//-----</Date>---------------------------------
1546
1547//-----<Newsgroups>----------------------------
1548
1549//@cond PRIVATE
1550kmime_mk_trivial_ctor_with_name_and_dptr( Newsgroups, Generics::Structured, Newsgroups )
1551kmime_mk_trivial_ctor_with_name( FollowUpTo, Newsgroups, Followup-To )
1552//@endcond
1553
1554QByteArray Newsgroups::as7BitString( bool withHeaderType ) const
1555{
1556 const Q_D( Newsgroups );
1557 if ( isEmpty() ) {
1558 return QByteArray();
1559 }
1560
1561 QByteArray rv;
1562 if ( withHeaderType ) {
1563 rv += typeIntro();
1564 }
1565
1566 for ( int i = 0; i < d->groups.count(); ++i ) {
1567 rv += d->groups[ i ];
1568 if ( i != d->groups.count() - 1 ) {
1569 rv += ',';
1570 }
1571 }
1572 return rv;
1573}
1574
1575void Newsgroups::fromUnicodeString( const QString &s, const QByteArray &b )
1576{
1577 Q_UNUSED( b );
1578 Q_D( Newsgroups );
1579 from7BitString( s.toUtf8() );
1580 d->encCS = cachedCharset( "UTF-8" );
1581}
1582
1583QString Newsgroups::asUnicodeString() const
1584{
1585 return QString::fromUtf8( as7BitString( false ) );
1586}
1587
1588void Newsgroups::clear()
1589{
1590 Q_D( Newsgroups );
1591 d->groups.clear();
1592}
1593
1594bool Newsgroups::isEmpty() const
1595{
1596 return d_func()->groups.isEmpty();
1597}
1598
1599QList<QByteArray> Newsgroups::groups() const
1600{
1601 return d_func()->groups;
1602}
1603
1604void Newsgroups::setGroups( const QList<QByteArray> &groups )
1605{
1606 Q_D( Newsgroups );
1607 d->groups = groups;
1608}
1609
1610bool Newsgroups::isCrossposted() const
1611{
1612 return d_func()->groups.count() >= 2;
1613}
1614
1615bool Newsgroups::parse( const char* &scursor, const char *const send, bool isCRLF )
1616{
1617 Q_D( Newsgroups );
1618 clear();
1619 forever {
1620 eatCFWS( scursor, send, isCRLF );
1621 if ( scursor != send && *scursor == ',' ) {
1622 ++scursor;
1623 }
1624 eatCFWS( scursor, send, isCRLF );
1625 if ( scursor == send ) {
1626 return true;
1627 }
1628 const char *start = scursor;
1629 while ( scursor != send && !isspace( *scursor ) && *scursor != ',' ) {
1630 ++scursor;
1631 }
1632 QByteArray group( start, scursor - start );
1633 d->groups.append( group );
1634 }
1635 return true;
1636}
1637
1638//-----</Newsgroups>---------------------------
1639
1640//-----<Lines>---------------------------------
1641
1642//@cond PRIVATE
1643kmime_mk_trivial_ctor_with_name_and_dptr( Lines, Generics::Structured, Lines )
1644//@endcond
1645
1646QByteArray Lines::as7BitString( bool withHeaderType ) const
1647{
1648 if ( isEmpty() ) {
1649 return QByteArray();
1650 }
1651
1652 QByteArray num;
1653 num.setNum( d_func()->lines );
1654
1655 if ( withHeaderType ) {
1656 return typeIntro() + num;
1657 }
1658 return num;
1659}
1660
1661QString Lines::asUnicodeString() const
1662{
1663 if ( isEmpty() ) {
1664 return QString();
1665 }
1666 return QString::number( d_func()->lines );
1667}
1668
1669void Lines::clear()
1670{
1671 Q_D( Lines );
1672 d->lines = -1;
1673}
1674
1675bool Lines::isEmpty() const
1676{
1677 return d_func()->lines == -1;
1678}
1679
1680int Lines::numberOfLines() const
1681{
1682 return d_func()->lines;
1683}
1684
1685void Lines::setNumberOfLines( int lines )
1686{
1687 Q_D( Lines );
1688 d->lines = lines;
1689}
1690
1691bool Lines::parse( const char* &scursor, const char* const send, bool isCRLF )
1692{
1693 Q_D( Lines );
1694 eatCFWS( scursor, send, isCRLF );
1695 if ( parseDigits( scursor, send, d->lines ) == 0 ) {
1696 clear();
1697 return false;
1698 }
1699 return true;
1700}
1701
1702//-----</Lines>--------------------------------
1703
1704//-----<Content-Type>--------------------------
1705
1706//@cond PRIVATE
1707kmime_mk_trivial_ctor_with_name_and_dptr( ContentType, Generics::Parametrized,
1708 Content-Type )
1709//@endcond
1710
1711bool ContentType::isEmpty() const
1712{
1713 return d_func()->mimeType.isEmpty();
1714}
1715
1716void ContentType::clear()
1717{
1718 Q_D( ContentType );
1719 d->category = CCsingle;
1720 d->mimeType.clear();
1721 Parametrized::clear();
1722}
1723
1724QByteArray ContentType::as7BitString( bool withHeaderType ) const
1725{
1726 if ( isEmpty() ) {
1727 return QByteArray();
1728 }
1729
1730 QByteArray rv;
1731 if ( withHeaderType ) {
1732 rv += typeIntro();
1733 }
1734
1735 rv += mimeType();
1736 if ( !Parametrized::isEmpty() ) {
1737 rv += "; " + Parametrized::as7BitString( false );
1738 }
1739
1740 return rv;
1741}
1742
1743QByteArray ContentType::mimeType() const
1744{
1745 Q_D( const ContentType );
1746 return d->mimeType;
1747}
1748
1749QByteArray ContentType::mediaType() const
1750{
1751 Q_D( const ContentType );
1752 const int pos = d->mimeType.indexOf( '/' );
1753 if ( pos < 0 ) {
1754 return d->mimeType;
1755 } else {
1756 return d->mimeType.left( pos );
1757 }
1758}
1759
1760QByteArray ContentType::subType() const
1761{
1762 Q_D( const ContentType );
1763 const int pos = d->mimeType.indexOf( '/' );
1764 if ( pos < 0 ) {
1765 return QByteArray();
1766 } else {
1767 return d->mimeType.mid( pos + 1 );
1768 }
1769}
1770
1771void ContentType::setMimeType( const QByteArray &mimeType )
1772{
1773 Q_D( ContentType );
1774 d->mimeType = mimeType;
1775 Parametrized::clear();
1776
1777 if ( isMultipart() ) {
1778 d->category = CCcontainer;
1779 } else {
1780 d->category = CCsingle;
1781 }
1782}
1783
1784bool ContentType::isMediatype( const char *mediatype ) const
1785{
1786 Q_D( const ContentType );
1787 const int len = strlen( mediatype );
1788 return qstrnicmp( d->mimeType.constData(), mediatype, len ) == 0 &&
1789 ( d->mimeType.at( len ) == '/' || d->mimeType.size() == len );
1790}
1791
1792bool ContentType::isSubtype( const char *subtype ) const
1793{
1794 Q_D( const ContentType );
1795 const int pos = d->mimeType.indexOf( '/' );
1796 if ( pos < 0 ) {
1797 return false;
1798 }
1799 const int len = strlen( subtype );
1800 return qstrnicmp( d->mimeType.constData() + pos + 1, subtype, len ) == 0 &&
1801 d->mimeType.size() == pos + len + 1;
1802}
1803
1804bool ContentType::isText() const
1805{
1806 return ( isMediatype( "text" ) || isEmpty() );
1807}
1808
1809bool ContentType::isPlainText() const
1810{
1811 return ( qstricmp( d_func()->mimeType.constData(), "text/plain" ) == 0 || isEmpty() );
1812}
1813
1814bool ContentType::isHTMLText() const
1815{
1816 return qstricmp( d_func()->mimeType.constData(), "text/html" ) == 0;
1817}
1818
1819bool ContentType::isImage() const
1820{
1821 return isMediatype( "image" );
1822}
1823
1824bool ContentType::isMultipart() const
1825{
1826 return isMediatype( "multipart" );
1827}
1828
1829bool ContentType::isPartial() const
1830{
1831 return qstricmp( d_func()->mimeType.constData(), "message/partial" ) == 0;
1832}
1833
1834QByteArray ContentType::charset() const
1835{
1836 QByteArray ret = parameter( QLatin1String( "charset" ) ).toLatin1();
1837 if ( ret.isEmpty() || forceDefaultCharset() ) {
1838 //return the default-charset if necessary
1839 ret = defaultCharset();
1840 }
1841 return ret;
1842}
1843
1844void ContentType::setCharset( const QByteArray &s )
1845{
1846 setParameter( QLatin1String( "charset" ), QString::fromLatin1( s ) );
1847}
1848
1849QByteArray ContentType::boundary() const
1850{
1851 return parameter( QLatin1String( "boundary" ) ).toLatin1();
1852}
1853
1854void ContentType::setBoundary( const QByteArray &s )
1855{
1856 setParameter( QLatin1String( "boundary" ), QString::fromLatin1( s ) );
1857}
1858
1859QString ContentType::name() const
1860{
1861 return parameter( QLatin1String( "name" ) );
1862}
1863
1864void ContentType::setName( const QString &s, const QByteArray &cs )
1865{
1866 Q_D( ContentType );
1867 d->encCS = cs;
1868 setParameter( QLatin1String( "name" ), s );
1869}
1870
1871QByteArray ContentType::id() const
1872{
1873 return parameter( QLatin1String( "id" ) ).toLatin1();
1874}
1875
1876void ContentType::setId( const QByteArray &s )
1877{
1878 setParameter( QLatin1String( "id" ), QString::fromLatin1( s ) );
1879}
1880
1881int ContentType::partialNumber() const
1882{
1883 QByteArray p = parameter( QLatin1String( "number" ) ).toLatin1();
1884 if ( !p.isEmpty() ) {
1885 return p.toInt();
1886 } else {
1887 return -1;
1888 }
1889}
1890
1891int ContentType::partialCount() const
1892{
1893 QByteArray p = parameter( QLatin1String( "total" ) ).toLatin1();
1894 if ( !p.isEmpty() ) {
1895 return p.toInt();
1896 } else {
1897 return -1;
1898 }
1899}
1900
1901contentCategory ContentType::category() const
1902{
1903 return d_func()->category;
1904}
1905
1906void ContentType::setCategory( contentCategory c )
1907{
1908 Q_D( ContentType );
1909 d->category = c;
1910}
1911
1912void ContentType::setPartialParams( int total, int number )
1913{
1914 setParameter( QLatin1String( "number" ), QString::number( number ) );
1915 setParameter( QLatin1String( "total" ), QString::number( total ) );
1916}
1917
1918bool ContentType::parse( const char* &scursor, const char * const send,
1919 bool isCRLF )
1920{
1921 Q_D( ContentType );
1922 // content-type: type "/" subtype *(";" parameter)
1923
1924 clear();
1925 eatCFWS( scursor, send, isCRLF );
1926 if ( scursor == send ) {
1927 return false; // empty header
1928 }
1929
1930 // type
1931 QPair<const char*, int> maybeMimeType;
1932 if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) {
1933 return false;
1934 }
1935
1936 // subtype
1937 eatCFWS( scursor, send, isCRLF );
1938 if ( scursor == send || *scursor != '/' ) {
1939 return false;
1940 }
1941 scursor++;
1942 eatCFWS( scursor, send, isCRLF );
1943 if ( scursor == send ) {
1944 return false;
1945 }
1946
1947 QPair<const char*, int> maybeSubType;
1948 if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) {
1949 return false;
1950 }
1951
1952 d->mimeType.reserve( maybeMimeType.second + maybeSubType.second + 1 );
1953 d->mimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower()
1954 + '/' + QByteArray( maybeSubType.first, maybeSubType.second ).toLower();
1955
1956 // parameter list
1957 eatCFWS( scursor, send, isCRLF );
1958 if ( scursor == send ) {
1959 goto success; // no parameters
1960 }
1961
1962 if ( *scursor != ';' ) {
1963 return false;
1964 }
1965 scursor++;
1966
1967 if ( !Parametrized::parse( scursor, send, isCRLF ) ) {
1968 return false;
1969 }
1970
1971 // adjust category
1972success:
1973 if ( isMultipart() ) {
1974 d->category = CCcontainer;
1975 } else {
1976 d->category = CCsingle;
1977 }
1978 return true;
1979}
1980
1981//-----</Content-Type>-------------------------
1982
1983//-----<ContentID>----------------------
1984
1985kmime_mk_trivial_ctor_with_name_and_dptr( ContentID, SingleIdent, Content-ID )
1986kmime_mk_dptr_ctor( ContentID, SingleIdent )
1987
1988bool ContentID::parse( const char* &scursor, const char *const send, bool isCRLF )
1989{
1990 Q_D ( ContentID );
1991 // Content-id := "<" contentid ">"
1992 // contentid := now whitespaces
1993
1994 const char* origscursor = scursor;
1995 if ( !SingleIdent::parse ( scursor, send, isCRLF ) ) {
1996 scursor = origscursor;
1997 d->msgIdList.clear();
1998 d->cachedIdentifier.clear();
1999
2000 while ( scursor != send ) {
2001 eatCFWS ( scursor, send, isCRLF );
2002 // empty entry ending the list: OK.
2003 if ( scursor == send ) {
2004 return true;
2005 }
2006 // empty entry: ignore.
2007 if ( *scursor == ',' ) {
2008 scursor++;
2009 continue;
2010 }
2011
2012 AddrSpec maybeContentId;
2013 // Almost parseAngleAddr
2014 if ( scursor == send || *scursor != '<' ) {
2015 return false;
2016 }
2017 scursor++; // eat '<'
2018
2019 eatCFWS ( scursor, send, isCRLF );
2020 if ( scursor == send ) {
2021 return false;
2022 }
2023
2024 // Save chars untill '>''
2025 QString result;
2026 if( !parseDotAtom(scursor, send, result, false) ) {
2027 return false;
2028 }
2029
2030 eatCFWS ( scursor, send, isCRLF );
2031 if ( scursor == send || *scursor != '>' ) {
2032 return false;
2033 }
2034 scursor++;
2035 // /Almost parseAngleAddr
2036
2037 maybeContentId.localPart = result;
2038 d->msgIdList.append ( maybeContentId );
2039
2040 eatCFWS ( scursor, send, isCRLF );
2041 // header end ending the list: OK.
2042 if ( scursor == send ) {
2043 return true;
2044 }
2045 // regular item separator: eat it.
2046 if ( *scursor == ',' ) {
2047 scursor++;
2048 }
2049 }
2050 return true;
2051 }
2052 else
2053 {
2054 return true;
2055 }
2056}
2057
2058//-----</ContentID>----------------------
2059
2060//-----<ContentTransferEncoding>----------------------------
2061
2062//@cond PRIVATE
2063kmime_mk_trivial_ctor_with_name_and_dptr( ContentTransferEncoding,
2064 Generics::Token, Content-Transfer-Encoding )
2065//@endcond
2066
2067typedef struct { const char *s; int e; } encTableType;
2068
2069static const encTableType encTable[] =
2070{
2071 { "7Bit", CE7Bit },
2072 { "8Bit", CE8Bit },
2073 { "quoted-printable", CEquPr },
2074 { "base64", CEbase64 },
2075 { "x-uuencode", CEuuenc },
2076 { "binary", CEbinary },
2077 { 0, 0}
2078};
2079
2080void ContentTransferEncoding::clear()
2081{
2082 Q_D( ContentTransferEncoding );
2083 d->decoded = true;
2084 d->cte = CE7Bit;
2085 Token::clear();
2086}
2087
2088contentEncoding ContentTransferEncoding::encoding() const
2089{
2090 return d_func()->cte;
2091}
2092
2093void ContentTransferEncoding::setEncoding( contentEncoding e )
2094{
2095 Q_D( ContentTransferEncoding );
2096 d->cte = e;
2097
2098 for ( int i = 0; encTable[i].s != 0; ++i ) {
2099 if ( d->cte == encTable[i].e ) {
2100 setToken( encTable[i].s );
2101 break;
2102 }
2103 }
2104}
2105
2106bool ContentTransferEncoding::decoded() const
2107{
2108 return d_func()->decoded;
2109}
2110
2111void ContentTransferEncoding::setDecoded( bool decoded )
2112{
2113 Q_D( ContentTransferEncoding );
2114 d->decoded = decoded;
2115}
2116
2117bool ContentTransferEncoding::needToEncode() const
2118{
2119 const Q_D( ContentTransferEncoding );
2120 return d->decoded && ( d->cte == CEquPr || d->cte == CEbase64 );
2121}
2122
2123bool ContentTransferEncoding::parse( const char *& scursor,
2124 const char * const send, bool isCRLF )
2125{
2126 Q_D( ContentTransferEncoding );
2127 clear();
2128 if ( !Token::parse( scursor, send, isCRLF ) ) {
2129 return false;
2130 }
2131
2132 // TODO: error handling in case of an unknown encoding?
2133 for ( int i = 0; encTable[i].s != 0; ++i ) {
2134 if ( qstricmp( token().constData(), encTable[i].s ) == 0 ) {
2135 d->cte = ( contentEncoding )encTable[i].e;
2136 break;
2137 }
2138 }
2139 d->decoded = ( d->cte == CE7Bit || d->cte == CE8Bit );
2140 return true;
2141}
2142
2143//-----</ContentTransferEncoding>---------------------------
2144
2145//-----<ContentDisposition>--------------------------
2146
2147//@cond PRIVATE
2148kmime_mk_trivial_ctor_with_name_and_dptr( ContentDisposition,
2149 Generics::Parametrized, Content-Disposition )
2150//@endcond
2151
2152QByteArray ContentDisposition::as7BitString( bool withHeaderType ) const
2153{
2154 if ( isEmpty() ) {
2155 return QByteArray();
2156 }
2157
2158 QByteArray rv;
2159 if ( withHeaderType ) {
2160 rv += typeIntro();
2161 }
2162
2163 if ( d_func()->disposition == CDattachment ) {
2164 rv += "attachment";
2165 } else if ( d_func()->disposition == CDinline ) {
2166 rv += "inline";
2167 } else {
2168 return QByteArray();
2169 }
2170
2171 if ( !Parametrized::isEmpty() ) {
2172 rv += "; " + Parametrized::as7BitString( false );
2173 }
2174
2175 return rv;
2176}
2177
2178bool ContentDisposition::isEmpty() const
2179{
2180 return d_func()->disposition == CDInvalid;
2181}
2182
2183void ContentDisposition::clear()
2184{
2185 Q_D( ContentDisposition );
2186 d->disposition = CDInvalid;
2187 Parametrized::clear();
2188}
2189
2190contentDisposition ContentDisposition::disposition() const
2191{
2192 return d_func()->disposition;
2193}
2194
2195void ContentDisposition::setDisposition( contentDisposition disp )
2196{
2197 Q_D( ContentDisposition );
2198 d->disposition = disp;
2199}
2200
2201QString KMime::Headers::ContentDisposition::filename() const
2202{
2203 return parameter( QLatin1String( "filename" ) );
2204}
2205
2206void ContentDisposition::setFilename( const QString &filename )
2207{
2208 setParameter( QLatin1String( "filename" ), filename );
2209}
2210
2211bool ContentDisposition::parse( const char *& scursor, const char * const send,
2212 bool isCRLF )
2213{
2214 Q_D( ContentDisposition );
2215 clear();
2216
2217 // token
2218 QByteArray token;
2219 eatCFWS( scursor, send, isCRLF );
2220 if ( scursor == send ) {
2221 return false;
2222 }
2223
2224 QPair<const char*, int> maybeToken;
2225 if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) {
2226 return false;
2227 }
2228
2229 token = QByteArray( maybeToken.first, maybeToken.second ).toLower();
2230 if ( token == "inline" ) {
2231 d->disposition = CDinline;
2232 } else if ( token == "attachment" ) {
2233 d->disposition = CDattachment;
2234 } else {
2235 return false;
2236 }
2237
2238 // parameter list
2239 eatCFWS( scursor, send, isCRLF );
2240 if ( scursor == send ) {
2241 return true; // no parameters
2242 }
2243
2244 if ( *scursor != ';' ) {
2245 return false;
2246 }
2247 scursor++;
2248
2249 return Parametrized::parse( scursor, send, isCRLF );
2250}
2251
2252//-----</ContentDisposition>-------------------------
2253
2254//@cond PRIVATE
2255kmime_mk_trivial_ctor_with_name( Subject, Generics::Unstructured, Subject )
2256//@endcond
2257
2258bool Subject::isReply() const
2259{
2260 return asUnicodeString().indexOf( QLatin1String( "Re:" ), 0, Qt::CaseInsensitive ) == 0;
2261}
2262
2263Base* createHeader( const QByteArray& type )
2264{
2265 return HeaderFactory::self()->createHeader( type );
2266}
2267
2268
2269//@cond PRIVATE
2270kmime_mk_trivial_ctor_with_name( ContentDescription,
2271 Generics::Unstructured, Content-Description )
2272kmime_mk_trivial_ctor_with_name( ContentLocation,
2273 Generics::Unstructured, Content-Location )
2274kmime_mk_trivial_ctor_with_name( From, Generics::MailboxList, From )
2275kmime_mk_trivial_ctor_with_name( Sender, Generics::SingleMailbox, Sender )
2276kmime_mk_trivial_ctor_with_name( To, Generics::AddressList, To )
2277kmime_mk_trivial_ctor_with_name( Cc, Generics::AddressList, Cc )
2278kmime_mk_trivial_ctor_with_name( Bcc, Generics::AddressList, Bcc )
2279kmime_mk_trivial_ctor_with_name( ReplyTo, Generics::AddressList, Reply-To )
2280kmime_mk_trivial_ctor_with_name( Keywords, Generics::PhraseList, Keywords )
2281kmime_mk_trivial_ctor_with_name( MIMEVersion, Generics::DotAtom, MIME-Version )
2282kmime_mk_trivial_ctor_with_name( Supersedes, Generics::SingleIdent, Supersedes )
2283kmime_mk_trivial_ctor_with_name( InReplyTo, Generics::Ident, In-Reply-To )
2284kmime_mk_trivial_ctor_with_name( References, Generics::Ident, References )
2285kmime_mk_trivial_ctor_with_name( Organization, Generics::Unstructured, Organization )
2286kmime_mk_trivial_ctor_with_name( UserAgent, Generics::Unstructured, User-Agent )
2287//@endcond
2288
2289} // namespace Headers
2290
2291} // namespace KMime
2292