• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.4 API Reference
  • KDE Home
  • Contact Us
 

KMIME Library

  • kmime
kmime_headers.cpp
Go to the documentation of this file.
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 */
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 
61 template <typename T>
62 bool 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 ) \
74 namespace { const bool dummyForRegistering##subclass = registerHeaderHelper<subclass>(); }
75 
76 // macro to generate a default constructor implementation
77 #define kmime_mk_trivial_ctor( subclass, baseclass ) \
78 subclass::subclass( Content *parent ) : baseclass( parent ) \
79 { \
80  clear(); \
81 } \
82  \
83 subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( parent ) \
84 { \
85  from7BitString( s ); \
86 } \
87  \
88 subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
89  baseclass( parent ) \
90 { \
91  fromUnicodeString( s, charset ); \
92 } \
93  \
94 subclass::~subclass() {} \
95  \
96 kmime_register_header( subclass )
97 // end kmime_mk_trivial_ctor
98 
99 
100 #define kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
101 subclass::subclass( Content *parent ) : baseclass( new subclass##Private, parent ) \
102 { \
103  clear(); \
104 } \
105  \
106 subclass::subclass( Content *parent, const QByteArray &s ) : baseclass( new subclass##Private, parent ) \
107 { \
108  from7BitString( s ); \
109 } \
110  \
111 subclass::subclass( Content *parent, const QString &s, const QByteArray &charset ) : \
112  baseclass( new subclass##Private, parent ) \
113 { \
114  fromUnicodeString( s, charset ); \
115 } \
116  \
117 subclass::~subclass() {} \
118  \
119 kmime_register_header( subclass )
120 // end kmime_mk_trivial_ctor_with_dptr
121 
122 
123 #define kmime_mk_trivial_ctor_with_name( subclass, baseclass, name ) \
124 kmime_mk_trivial_ctor( subclass, baseclass ) \
125  \
126 const char *subclass::type() const \
127 { \
128  return staticType(); \
129 } \
130 const char *subclass::staticType() { return #name; }
131 
132 #define kmime_mk_trivial_ctor_with_name_and_dptr( subclass, baseclass, name ) \
133 kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \
134 const char *subclass::type() const { return staticType(); } \
135 const char *subclass::staticType() { return #name; }
136 
137 #define kmime_mk_dptr_ctor( subclass, baseclass ) \
138 subclass::subclass( subclass##Private *d, KMime::Content *parent ) : baseclass( d, parent ) {}
139 
140 using namespace KMime;
141 using namespace KMime::Headers;
142 using namespace KMime::Types;
143 using namespace KMime::HeaderParsing;
144 
145 namespace KMime {
146 namespace Headers {
147 //-----<Base>----------------------------------
148 Base::Base( KMime::Content *parent ) :
149  d_ptr( new BasePrivate )
150 {
151  Q_D(Base);
152  d->parent = parent;
153 }
154 
155 Base::Base( BasePrivate *dd, KMime::Content *parent ) :
156  d_ptr( dd )
157 {
158  Q_D(Base);
159  d->parent = parent;
160 }
161 
162 Base::~Base()
163 {
164  delete d_ptr;
165  d_ptr = 0;
166 }
167 
168 KMime::Content *Base::parent() const
169 {
170  return d_ptr->parent;
171 }
172 
173 void Base::setParent( KMime::Content *parent )
174 {
175  d_ptr->parent = parent;
176 }
177 
178 QByteArray Base::rfc2047Charset() const
179 {
180  if ( d_ptr->encCS.isEmpty() || forceDefaultCharset() ) {
181  return defaultCharset();
182  } else {
183  return d_ptr->encCS;
184  }
185 }
186 
187 void Base::setRFC2047Charset( const QByteArray &cs )
188 {
189  d_ptr->encCS = cachedCharset( cs );
190 }
191 
192 bool Base::forceDefaultCharset() const
193 {
194  return ( parent() != 0 ? parent()->forceDefaultCharset() : false );
195 }
196 
197 QByteArray Base::defaultCharset() const
198 {
199  return ( parent() != 0 ? parent()->defaultCharset() : Latin1 );
200 }
201 
202 const char *Base::type() const
203 {
204  return "";
205 }
206 
207 bool Base::is( const char *t ) const
208 {
209  return qstricmp( t, type() ) == 0;
210 }
211 
212 bool Base::isMimeHeader() const
213 {
214  return qstrnicmp( type(), "Content-", 8 ) == 0;
215 }
216 
217 bool Base::isXHeader() const
218 {
219  return qstrncmp( type(), "X-", 2 ) == 0;
220 }
221 
222 QByteArray Base::typeIntro() const
223 {
224  return QByteArray( type() ) + ": ";
225 }
226 
227 //-----</Base>---------------------------------
228 
229 namespace Generics {
230 
231 //-----<Unstructured>-------------------------
232 
233 //@cond PRIVATE
234 kmime_mk_dptr_ctor( Unstructured, Base )
235 //@endcond
236 
237 Unstructured::Unstructured( Content *p ) : Base( new UnstructuredPrivate, p )
238 {
239 }
240 
241 Unstructured::Unstructured( Content *p, const QByteArray &s ) : Base( new UnstructuredPrivate, p )
242 {
243  from7BitString( s );
244 }
245 
246 Unstructured::Unstructured( Content *p, const QString &s, const QByteArray &cs ) : Base( new UnstructuredPrivate, p )
247 {
248  fromUnicodeString( s, cs );
249 }
250 
251 Unstructured::~Unstructured()
252 {
253 }
254 
255 void Unstructured::from7BitString( const QByteArray &s )
256 {
257  Q_D(Unstructured);
258  d->decoded = decodeRFC2047String( s, d->encCS, defaultCharset(), forceDefaultCharset() );
259 }
260 
261 QByteArray 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 
273 void Unstructured::fromUnicodeString( const QString &s, const QByteArray &b )
274 {
275  Q_D(Unstructured);
276  d->decoded = s;
277  d->encCS = cachedCharset( b );
278 }
279 
280 QString Unstructured::asUnicodeString() const
281 {
282  return d_func()->decoded;
283 }
284 
285 void Unstructured::clear()
286 {
287  Q_D(Unstructured);
288  d->decoded.truncate( 0 );
289 }
290 
291 bool Unstructured::isEmpty() const
292 {
293  return d_func()->decoded.isEmpty();
294 }
295 
296 //-----</Unstructured>-------------------------
297 
298 //-----<Structured>-------------------------
299 
300 Structured::Structured( Content *p ) : Base( new StructuredPrivate, p )
301 {
302 }
303 
304 Structured::Structured( Content *p, const QByteArray &s ) : Base( new StructuredPrivate, p )
305 {
306  from7BitString( s );
307 }
308 
309 Structured::Structured( Content *p, const QString &s, const QByteArray &cs ) : Base( new StructuredPrivate, p )
310 {
311  fromUnicodeString( s, cs );
312 }
313 
314 kmime_mk_dptr_ctor( Structured, Base )
315 
316 Structured::~Structured()
317 {
318 }
319 
320 void 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 
330 QString Structured::asUnicodeString() const
331 {
332  return QString::fromLatin1( as7BitString( false ) );
333 }
334 
335 void 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 
346 Address::Address( Content *p ) : Structured( new AddressPrivate, p )
347 {
348 }
349 
350 Address::Address( Content *p, const QByteArray &s ) : Structured( new AddressPrivate, p )
351 {
352  from7BitString( s );
353 }
354 
355 Address::Address( Content *p, const QString &s, const QByteArray &cs ) : Structured( new AddressPrivate, p )
356 {
357  fromUnicodeString( s, cs );
358 }
359 
360 kmime_mk_dptr_ctor( Address, Structured )
361 
362 Address:: ~Address()
363 {
364 }
365 
366 // helper method used in AddressList and MailboxList
367 static 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 
387 kmime_mk_trivial_ctor_with_dptr( MailboxList, Address )
388 kmime_mk_dptr_ctor( MailboxList, Address )
389 
390 QByteArray 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 
409 void 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 
416 QString MailboxList::asUnicodeString() const
417 {
418  return prettyAddresses().join( QLatin1String( ", " ) );
419 }
420 
421 void MailboxList::clear()
422 {
423  Q_D(MailboxList);
424  d->mailboxList.clear();
425 }
426 
427 bool MailboxList::isEmpty() const
428 {
429  return d_func()->mailboxList.isEmpty();
430 }
431 
432 void MailboxList::addAddress( const Types::Mailbox &mbox )
433 {
434  Q_D(MailboxList);
435  d->mailboxList.append( mbox );
436 }
437 
438 void 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 
448 QList< 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 
457 QStringList 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 
466 QStringList 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 
475 Types::Mailbox::List MailboxList::mailboxes() const
476 {
477  return d_func()->mailboxList;
478 }
479 
480 bool 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
513 kmime_mk_trivial_ctor_with_dptr( SingleMailbox, MailboxList )
514 //@endcond
515 
516 bool 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
536 kmime_mk_trivial_ctor_with_dptr( AddressList, Address )
537 kmime_mk_dptr_ctor( AddressList, Address )
538 //@endcond
539 
540 QByteArray 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 
561 void 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 
568 QString AddressList::asUnicodeString() const
569 {
570  return prettyAddresses().join( QLatin1String( ", " ) );
571 }
572 
573 void AddressList::clear()
574 {
575  Q_D(AddressList);
576  d->addressList.clear();
577 }
578 
579 bool AddressList::isEmpty() const
580 {
581  return d_func()->addressList.isEmpty();
582 }
583 
584 void 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 
592 void 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 
604 QList< 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 
615 QStringList 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 
626 QStringList 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 
637 Types::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 
648 bool 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
666 kmime_mk_trivial_ctor_with_dptr( Token, Structured )
667 kmime_mk_dptr_ctor( Token, Structured )
668 //@endcond
669 
670 QByteArray 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 
681 void Token::clear()
682 {
683  Q_D(Token);
684  d->token.clear();
685 }
686 
687 bool Token::isEmpty() const
688 {
689  return d_func()->token.isEmpty();
690 }
691 
692 QByteArray Token::token() const
693 {
694  return d_func()->token;
695 }
696 
697 void Token::setToken( const QByteArray &t )
698 {
699  Q_D(Token);
700  d->token = t;
701 }
702 
703 bool 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
733 kmime_mk_trivial_ctor_with_dptr( PhraseList, Structured )
734 //@endcond
735 
736 QByteArray 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 
759 QString PhraseList::asUnicodeString() const
760 {
761  return d_func()->phraseList.join( QLatin1String( ", " ) );
762 }
763 
764 void PhraseList::clear()
765 {
766  Q_D(PhraseList);
767  d->phraseList.clear();
768 }
769 
770 bool PhraseList::isEmpty() const
771 {
772  return d_func()->phraseList.isEmpty();
773 }
774 
775 QStringList PhraseList::phrases() const
776 {
777  return d_func()->phraseList;
778 }
779 
780 bool 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
822 kmime_mk_trivial_ctor_with_dptr( DotAtom, Structured )
823 //@endcond
824 
825 QByteArray 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 
840 QString DotAtom::asUnicodeString() const
841 {
842  return d_func()->dotAtom;
843 }
844 
845 void DotAtom::clear()
846 {
847  Q_D(DotAtom);
848  d->dotAtom.clear();
849 }
850 
851 bool DotAtom::isEmpty() const
852 {
853  return d_func()->dotAtom.isEmpty();
854 }
855 
856 bool 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
880 kmime_mk_trivial_ctor_with_dptr( Parametrized, Structured )
881 kmime_mk_dptr_ctor( Parametrized, Structured )
882 //@endcond
883 
884 QByteArray 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  {
900  if ( !first ) {
901  rv += "; ";
902  } else {
903  first = false;
904  }
905  if ( isUsAscii( it.value() ) ) {
906  rv += it.key().toLatin1() + '=';
907  QByteArray tmp = it.value().toLatin1();
908  addQuotes( tmp, true ); // force quoting, eg. for whitespaces in parameter value
909  rv += tmp;
910  } else {
911  if( useOutlookAttachmentEncoding() ) {
912  rv += it.key().toLatin1() + '=';
913  kDebug() << "doing:" << it.value() << QLatin1String( d->encCS );
914  rv += "\"" + encodeRFC2047String( it.value(), d->encCS ) + "\"";
915  } else {
916  rv += it.key().toLatin1() + "*=";
917  rv += encodeRFC2231String( it.value(), d->encCS );
918  }
919  }
920  }
921 
922  return rv;
923 }
924 
925 QString Parametrized::parameter( const QString &key ) const
926 {
927  return d_func()->parameterHash.value( key.toLower() );
928 }
929 
930 bool Parametrized::hasParameter( const QString &key ) const
931 {
932  return d_func()->parameterHash.contains( key.toLower() );
933 }
934 
935 void Parametrized::setParameter( const QString &key, const QString &value )
936 {
937  Q_D(Parametrized);
938  d->parameterHash.insert( key.toLower(), value );
939 }
940 
941 bool Parametrized::isEmpty() const
942 {
943  return d_func()->parameterHash.isEmpty();
944 }
945 
946 void Parametrized::clear()
947 {
948  Q_D(Parametrized);
949  d->parameterHash.clear();
950 }
951 
952 bool Parametrized::parse( const char *& scursor, const char * const send,
953  bool isCRLF )
954 {
955  Q_D(Parametrized);
956  d->parameterHash.clear();
957  QByteArray charset;
958  if ( !parseParameterListWithCharset( scursor, send, d->parameterHash, charset, isCRLF ) ) {
959  return false;
960  }
961  d->encCS = charset;
962  return true;
963 }
964 
965 //-----</Parametrized>-------------------------
966 
967 //-----<Ident>-------------------------
968 
969 //@cond PRIVATE
970 kmime_mk_trivial_ctor_with_dptr( Ident, Address )
971 kmime_mk_dptr_ctor( Ident, Address )
972 //@endcond
973 
974 QByteArray Ident::as7BitString( bool withHeaderType ) const
975 {
976  const Q_D(Ident);
977  if ( d->msgIdList.isEmpty() ) {
978  return QByteArray();
979  }
980 
981  QByteArray rv;
982  if ( withHeaderType ) {
983  rv = typeIntro();
984  }
985  foreach ( const Types::AddrSpec &addr, d->msgIdList ) {
986  if ( !addr.isEmpty() ) {
987  const QString asString = addr.asString();
988  rv += '<';
989  if ( !asString.isEmpty() ) {
990  rv += asString.toLatin1(); // FIXME: change parsing to use QByteArrays
991  }
992  rv += "> ";
993  }
994  }
995  if ( !rv.isEmpty() ) {
996  rv.resize( rv.length() - 1 );
997  }
998  return rv;
999 }
1000 
1001 void Ident::clear()
1002 {
1003  Q_D(Ident);
1004  d->msgIdList.clear();
1005  d->cachedIdentifier.clear();
1006 }
1007 
1008 bool Ident::isEmpty() const
1009 {
1010  return d_func()->msgIdList.isEmpty();
1011 }
1012 
1013 bool Ident::parse( const char* &scursor, const char * const send, bool isCRLF )
1014 {
1015  Q_D(Ident);
1016  // msg-id := "<" id-left "@" id-right ">"
1017  // id-left := dot-atom-text / no-fold-quote / local-part
1018  // id-right := dot-atom-text / no-fold-literal / domain
1019  //
1020  // equivalent to:
1021  // msg-id := angle-addr
1022 
1023  d->msgIdList.clear();
1024  d->cachedIdentifier.clear();
1025 
1026  while ( scursor != send ) {
1027  eatCFWS( scursor, send, isCRLF );
1028  // empty entry ending the list: OK.
1029  if ( scursor == send ) {
1030  return true;
1031  }
1032  // empty entry: ignore.
1033  if ( *scursor == ',' ) {
1034  scursor++;
1035  continue;
1036  }
1037 
1038  AddrSpec maybeMsgId;
1039  if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) {
1040  return false;
1041  }
1042  d->msgIdList.append( maybeMsgId );
1043 
1044  eatCFWS( scursor, send, isCRLF );
1045  // header end ending the list: OK.
1046  if ( scursor == send ) {
1047  return true;
1048  }
1049  // regular item separator: eat it.
1050  if ( *scursor == ',' ) {
1051  scursor++;
1052  }
1053  }
1054  return true;
1055 }
1056 
1057 QList<QByteArray> Ident::identifiers() const
1058 {
1059  QList<QByteArray> rv;
1060  foreach ( const Types::AddrSpec &addr, d_func()->msgIdList ) {
1061  if ( !addr.isEmpty() ) {
1062  const QString asString = addr.asString();
1063  if ( !asString.isEmpty() ) {
1064  rv.append( asString.toLatin1() ); // FIXME: change parsing to use QByteArrays
1065  }
1066  }
1067  }
1068  return rv;
1069 }
1070 
1071 void Ident::appendIdentifier( const QByteArray &id )
1072 {
1073  Q_D(Ident);
1074  QByteArray tmp = id;
1075  if ( !tmp.startsWith( '<' ) ) {
1076  tmp.prepend( '<' );
1077  }
1078  if ( !tmp.endsWith( '>' ) ) {
1079  tmp.append( '>' );
1080  }
1081  AddrSpec msgId;
1082  const char *cursor = tmp.constData();
1083  if ( parseAngleAddr( cursor, cursor + tmp.length(), msgId ) ) {
1084  d->msgIdList.append( msgId );
1085  } else {
1086  kWarning() << "Unable to parse address spec!";
1087  }
1088 }
1089 
1090 //-----</Ident>-------------------------
1091 
1092 //-----<SingleIdent>-------------------------
1093 
1094 //@cond PRIVATE
1095 kmime_mk_trivial_ctor_with_dptr( SingleIdent, Ident )
1096 kmime_mk_dptr_ctor( SingleIdent, Ident )
1097 //@endcond
1098 
1099 QByteArray SingleIdent::identifier() const
1100 {
1101  if ( d_func()->msgIdList.isEmpty() ) {
1102  return QByteArray();
1103  }
1104 
1105  if ( d_func()->cachedIdentifier.isEmpty() ) {
1106  const Types::AddrSpec &addr = d_func()->msgIdList.first();
1107  if ( !addr.isEmpty() ) {
1108  const QString asString = addr.asString();
1109  if ( !asString.isEmpty() ) {
1110  d_func()->cachedIdentifier = asString.toLatin1();// FIXME: change parsing to use QByteArrays
1111  }
1112  }
1113  }
1114 
1115  return d_func()->cachedIdentifier;
1116 }
1117 
1118 void SingleIdent::setIdentifier( const QByteArray &id )
1119 {
1120  Q_D(SingleIdent);
1121  d->msgIdList.clear();
1122  d->cachedIdentifier.clear();
1123  appendIdentifier( id );
1124 }
1125 
1126 bool SingleIdent::parse( const char* &scursor, const char * const send,
1127  bool isCRLF )
1128 {
1129  Q_D(SingleIdent);
1130  if ( !Ident::parse( scursor, send, isCRLF ) ) {
1131  return false;
1132  }
1133 
1134  if ( d->msgIdList.count() > 1 ) {
1135  KMIME_WARN << "more than one msg-id in header "
1136  << "allowing only a single one!" << endl;
1137  }
1138  return true;
1139 }
1140 
1141 //-----</SingleIdent>-------------------------
1142 
1143 } // namespace Generics
1144 
1145 //-----<ReturnPath>-------------------------
1146 
1147 //@cond PRIVATE
1148 kmime_mk_trivial_ctor_with_name_and_dptr( ReturnPath, Generics::Address, Return-Path )
1149 //@endcond
1150 
1151 QByteArray ReturnPath::as7BitString( bool withHeaderType ) const
1152 {
1153  if ( isEmpty() ) {
1154  return QByteArray();
1155  }
1156 
1157  QByteArray rv;
1158  if ( withHeaderType ) {
1159  rv += typeIntro();
1160  }
1161  rv += '<' + d_func()->mailbox.as7BitString( d_func()->encCS ) + '>';
1162  return rv;
1163 }
1164 
1165 void ReturnPath::clear()
1166 {
1167  Q_D(ReturnPath);
1168  d->mailbox.setAddress( Types::AddrSpec() );
1169  d->mailbox.setName( QString() );
1170 }
1171 
1172 bool ReturnPath::isEmpty() const
1173 {
1174  const Q_D(ReturnPath);
1175  return !d->mailbox.hasAddress() && !d->mailbox.hasName();
1176 }
1177 
1178 bool ReturnPath::parse( const char* &scursor, const char * const send,
1179  bool isCRLF )
1180 {
1181  Q_D(ReturnPath);
1182  eatCFWS( scursor, send, isCRLF );
1183  if ( scursor == send ) {
1184  return false;
1185  }
1186 
1187  const char * oldscursor = scursor;
1188 
1189  Mailbox maybeMailbox;
1190  if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
1191  // mailbox parsing failed, but check for empty brackets:
1192  scursor = oldscursor;
1193  if ( *scursor != '<' ) {
1194  return false;
1195  }
1196  scursor++;
1197  eatCFWS( scursor, send, isCRLF );
1198  if ( scursor == send || *scursor != '>' ) {
1199  return false;
1200  }
1201  scursor++;
1202 
1203  // prepare a Null mailbox:
1204  AddrSpec emptyAddrSpec;
1205  maybeMailbox.setName( QString() );
1206  maybeMailbox.setAddress( emptyAddrSpec );
1207  } else {
1208  // check that there was no display-name:
1209  if ( maybeMailbox.hasName() ) {
1210  KMIME_WARN << "display-name \"" << maybeMailbox.name()
1211  << "\" in Return-Path!" << endl;
1212  }
1213  }
1214  d->mailbox = maybeMailbox;
1215 
1216  // see if that was all:
1217  eatCFWS( scursor, send, isCRLF );
1218  // and warn if it wasn't:
1219  if ( scursor != send ) {
1220  KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl;
1221  }
1222  return true;
1223 }
1224 
1225 //-----</ReturnPath>-------------------------
1226 
1227 //-----<Generic>-------------------------------
1228 
1229 // NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable.
1230 
1231 Generic::Generic() : Generics::Unstructured( new GenericPrivate )
1232 {
1233 }
1234 
1235 Generic::Generic( const char *t ) : Generics::Unstructured( new GenericPrivate )
1236 {
1237  setType( t );
1238 }
1239 
1240 Generic::Generic( const char *t, Content *p )
1241  : Generics::Unstructured( new GenericPrivate, p )
1242 {
1243  setType( t );
1244 }
1245 
1246 Generic::Generic( const char *t, Content *p, const QByteArray &s )
1247  : Generics::Unstructured( new GenericPrivate, p )
1248 {
1249  from7BitString( s );
1250  setType( t );
1251 }
1252 
1253 Generic::Generic( const char *t, Content *p, const QString &s, const QByteArray &cs )
1254  : Generics::Unstructured( new GenericPrivate, p )
1255 {
1256  fromUnicodeString( s, cs );
1257  setType( t );
1258 }
1259 
1260 Generic::~Generic()
1261 {
1262 }
1263 
1264 void Generic::clear()
1265 {
1266  Q_D(Generic);
1267  delete[] d->type;
1268  d->type = 0;
1269  Unstructured::clear();
1270 }
1271 
1272 bool Generic::isEmpty() const
1273 {
1274  return d_func()->type == 0 || Unstructured::isEmpty();
1275 }
1276 
1277 const char *Generic::type() const
1278 {
1279  return d_func()->type;
1280 }
1281 
1282 void Generic::setType( const char *type )
1283 {
1284  Q_D(Generic);
1285  if ( d->type ) {
1286  delete[] d->type;
1287  }
1288  if ( type ) {
1289  d->type = new char[strlen( type )+1];
1290  strcpy( d->type, type );
1291  } else {
1292  d->type = 0;
1293  }
1294 }
1295 
1296 //-----<Generic>-------------------------------
1297 
1298 //-----<MessageID>-----------------------------
1299 
1300 //@cond PRIVATE
1301 kmime_mk_trivial_ctor_with_name( MessageID, Generics::SingleIdent, Message-ID )
1302 //@endcond
1303 
1304 void MessageID::generate( const QByteArray &fqdn )
1305 {
1306  setIdentifier( '<' + uniqueString() + '@' + fqdn + '>' );
1307 }
1308 
1309 //-----</MessageID>----------------------------
1310 
1311 //-----<Control>-------------------------------
1312 
1313 //@cond PRIVATE
1314 kmime_mk_trivial_ctor_with_name_and_dptr( Control, Generics::Structured, Control )
1315 //@endcond
1316 
1317 QByteArray Control::as7BitString( bool withHeaderType ) const
1318 {
1319  const Q_D(Control);
1320  if ( isEmpty() ) {
1321  return QByteArray();
1322  }
1323 
1324  QByteArray rv;
1325  if ( withHeaderType ) {
1326  rv += typeIntro();
1327  }
1328 
1329  rv += d->name;
1330  if ( !d->parameter.isEmpty() ) {
1331  rv += ' ' + d->parameter;
1332  }
1333  return rv;
1334 }
1335 
1336 void Control::clear()
1337 {
1338  Q_D(Control);
1339  d->name.clear();
1340  d->parameter.clear();
1341 }
1342 
1343 bool Control::isEmpty() const
1344 {
1345  return d_func()->name.isEmpty();
1346 }
1347 
1348 QByteArray Control::controlType() const
1349 {
1350  return d_func()->name;
1351 }
1352 
1353 QByteArray Control::parameter() const
1354 {
1355  return d_func()->parameter;
1356 }
1357 
1358 bool Control::isCancel() const
1359 {
1360  return d_func()->name.toLower() == "cancel";
1361 }
1362 
1363 void Control::setCancel( const QByteArray &msgid )
1364 {
1365  Q_D(Control);
1366  d->name = "cancel";
1367  d->parameter = msgid;
1368 }
1369 
1370 bool Control::parse( const char* &scursor, const char *const send, bool isCRLF )
1371 {
1372  Q_D(Control);
1373  clear();
1374  eatCFWS( scursor, send, isCRLF );
1375  if ( scursor == send ) {
1376  return false;
1377  }
1378  const char *start = scursor;
1379  while ( scursor != send && !isspace( *scursor ) ) {
1380  ++scursor;
1381  }
1382  d->name = QByteArray( start, scursor - start );
1383  eatCFWS( scursor, send, isCRLF );
1384  d->parameter = QByteArray( scursor, send - scursor );
1385  return true;
1386 }
1387 
1388 //-----</Control>------------------------------
1389 
1390 //-----<MailCopiesTo>--------------------------
1391 
1392 //@cond PRIVATE
1393 kmime_mk_trivial_ctor_with_name_and_dptr( MailCopiesTo,
1394  Generics::AddressList, Mail-Copies-To )
1395 //@endcond
1396 
1397 QByteArray MailCopiesTo::as7BitString( bool withHeaderType ) const
1398 {
1399  QByteArray rv;
1400  if ( withHeaderType ) {
1401  rv += typeIntro();
1402  }
1403  if ( !AddressList::isEmpty() ) {
1404  rv += AddressList::as7BitString( false );
1405  } else {
1406  if ( d_func()->alwaysCopy ) {
1407  rv += "poster";
1408  } else if ( d_func()->neverCopy ) {
1409  rv += "nobody";
1410  }
1411  }
1412  return rv;
1413 }
1414 
1415 QString MailCopiesTo::asUnicodeString() const
1416 {
1417  if ( !AddressList::isEmpty() ) {
1418  return AddressList::asUnicodeString();
1419  }
1420  if ( d_func()->alwaysCopy ) {
1421  return QLatin1String( "poster" );
1422  }
1423  if ( d_func()->neverCopy ) {
1424  return QLatin1String( "nobody" );
1425  }
1426  return QString();
1427 }
1428 
1429 void MailCopiesTo::clear()
1430 {
1431  Q_D(MailCopiesTo);
1432  AddressList::clear();
1433  d->alwaysCopy = false;
1434  d->neverCopy = false;
1435 }
1436 
1437 bool MailCopiesTo::isEmpty() const
1438 {
1439  return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy);
1440 }
1441 
1442 bool MailCopiesTo::alwaysCopy() const
1443 {
1444  return !AddressList::isEmpty() || d_func()->alwaysCopy;
1445 }
1446 
1447 void MailCopiesTo::setAlwaysCopy()
1448 {
1449  Q_D(MailCopiesTo);
1450  clear();
1451  d->alwaysCopy = true;
1452 }
1453 
1454 bool MailCopiesTo::neverCopy() const
1455 {
1456  return d_func()->neverCopy;
1457 }
1458 
1459 void MailCopiesTo::setNeverCopy()
1460 {
1461  Q_D(MailCopiesTo);
1462  clear();
1463  d->neverCopy = true;
1464 }
1465 
1466 bool MailCopiesTo::parse( const char *& scursor, const char * const send,
1467  bool isCRLF )
1468 {
1469  Q_D(MailCopiesTo);
1470  clear();
1471  if ( send - scursor == 5 ) {
1472  if ( qstrnicmp( "never", scursor, 5 ) == 0 ) {
1473  d->neverCopy = true;
1474  return true;
1475  }
1476  }
1477  if ( send - scursor == 6 ) {
1478  if ( qstrnicmp( "always", scursor, 6 ) == 0 || qstrnicmp( "poster", scursor, 6 ) == 0 ) {
1479  d->alwaysCopy = true;
1480  return true;
1481  }
1482  if ( qstrnicmp( "nobody", scursor, 6 ) == 0 ) {
1483  d->neverCopy = true;
1484  return true;
1485  }
1486  }
1487  return AddressList::parse( scursor, send, isCRLF );
1488 }
1489 
1490 //-----</MailCopiesTo>-------------------------
1491 
1492 //-----<Date>----------------------------------
1493 
1494 //@cond PRIVATE
1495 kmime_mk_trivial_ctor_with_name_and_dptr( Date, Generics::Structured, Date )
1496 //@endcond
1497 
1498 QByteArray Date::as7BitString( bool withHeaderType ) const
1499 {
1500  if ( isEmpty() ) {
1501  return QByteArray();
1502  }
1503 
1504  QByteArray rv;
1505  if ( withHeaderType ) {
1506  rv += typeIntro();
1507  }
1508  rv += d_func()->dateTime.toString( KDateTime::RFCDateDay ).toLatin1();
1509  return rv;
1510 }
1511 
1512 void Date::clear()
1513 {
1514  Q_D(Date);
1515  d->dateTime = KDateTime();
1516 }
1517 
1518 bool Date::isEmpty() const
1519 {
1520  return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid();
1521 }
1522 
1523 KDateTime Date::dateTime() const
1524 {
1525  return d_func()->dateTime;
1526 }
1527 
1528 void Date::setDateTime( const KDateTime &dt )
1529 {
1530  Q_D(Date);
1531  d->dateTime = dt;
1532 }
1533 
1534 int Date::ageInDays() const
1535 {
1536  QDate today = QDate::currentDate();
1537  return dateTime().date().daysTo(today);
1538 }
1539 
1540 bool Date::parse( const char* &scursor, const char *const send, bool isCRLF )
1541 {
1542  Q_D(Date);
1543  return parseDateTime( scursor, send, d->dateTime, isCRLF );
1544 }
1545 
1546 //-----</Date>---------------------------------
1547 
1548 //-----<Newsgroups>----------------------------
1549 
1550 //@cond PRIVATE
1551 kmime_mk_trivial_ctor_with_name_and_dptr( Newsgroups, Generics::Structured, Newsgroups )
1552 kmime_mk_trivial_ctor_with_name( FollowUpTo, Newsgroups, Followup-To )
1553 //@endcond
1554 
1555 QByteArray Newsgroups::as7BitString( bool withHeaderType ) const
1556 {
1557  const Q_D(Newsgroups);
1558  if ( isEmpty() ) {
1559  return QByteArray();
1560  }
1561 
1562  QByteArray rv;
1563  if ( withHeaderType ) {
1564  rv += typeIntro();
1565  }
1566 
1567  for ( int i = 0; i < d->groups.count(); ++i ) {
1568  rv += d->groups[ i ];
1569  if ( i != d->groups.count() - 1 ) {
1570  rv += ',';
1571  }
1572  }
1573  return rv;
1574 }
1575 
1576 void Newsgroups::fromUnicodeString( const QString &s, const QByteArray &b )
1577 {
1578  Q_UNUSED( b );
1579  Q_D(Newsgroups);
1580  from7BitString( s.toUtf8() );
1581  d->encCS = cachedCharset( "UTF-8" );
1582 }
1583 
1584 QString Newsgroups::asUnicodeString() const
1585 {
1586  return QString::fromUtf8( as7BitString( false ) );
1587 }
1588 
1589 void Newsgroups::clear()
1590 {
1591  Q_D(Newsgroups);
1592  d->groups.clear();
1593 }
1594 
1595 bool Newsgroups::isEmpty() const
1596 {
1597  return d_func()->groups.isEmpty();
1598 }
1599 
1600 QList<QByteArray> Newsgroups::groups() const
1601 {
1602  return d_func()->groups;
1603 }
1604 
1605 void Newsgroups::setGroups( const QList<QByteArray> &groups )
1606 {
1607  Q_D(Newsgroups);
1608  d->groups = groups;
1609 }
1610 
1611 bool Newsgroups::isCrossposted() const
1612 {
1613  return d_func()->groups.count() >= 2;
1614 }
1615 
1616 bool Newsgroups::parse( const char* &scursor, const char *const send, bool isCRLF )
1617 {
1618  Q_D(Newsgroups);
1619  clear();
1620  forever {
1621  eatCFWS( scursor, send, isCRLF );
1622  if ( scursor != send && *scursor == ',' ) {
1623  ++scursor;
1624  }
1625  eatCFWS( scursor, send, isCRLF );
1626  if ( scursor == send ) {
1627  return true;
1628  }
1629  const char *start = scursor;
1630  while ( scursor != send && !isspace( *scursor ) && *scursor != ',' ) {
1631  ++scursor;
1632  }
1633  QByteArray group( start, scursor - start );
1634  d->groups.append( group );
1635  }
1636  return true;
1637 }
1638 
1639 //-----</Newsgroups>---------------------------
1640 
1641 //-----<Lines>---------------------------------
1642 
1643 //@cond PRIVATE
1644 kmime_mk_trivial_ctor_with_name_and_dptr( Lines, Generics::Structured, Lines )
1645 //@endcond
1646 
1647 QByteArray Lines::as7BitString( bool withHeaderType ) const
1648 {
1649  if ( isEmpty() ) {
1650  return QByteArray();
1651  }
1652 
1653  QByteArray num;
1654  num.setNum( d_func()->lines );
1655 
1656  if ( withHeaderType ) {
1657  return typeIntro() + num;
1658  }
1659  return num;
1660 }
1661 
1662 QString Lines::asUnicodeString() const
1663 {
1664  if ( isEmpty() ) {
1665  return QString();
1666  }
1667  return QString::number( d_func()->lines );
1668 }
1669 
1670 void Lines::clear()
1671 {
1672  Q_D(Lines);
1673  d->lines = -1;
1674 }
1675 
1676 bool Lines::isEmpty() const
1677 {
1678  return d_func()->lines == -1;
1679 }
1680 
1681 int Lines::numberOfLines() const
1682 {
1683  return d_func()->lines;
1684 }
1685 
1686 void Lines::setNumberOfLines( int lines )
1687 {
1688  Q_D(Lines);
1689  d->lines = lines;
1690 }
1691 
1692 bool Lines::parse( const char* &scursor, const char* const send, bool isCRLF )
1693 {
1694  Q_D(Lines);
1695  eatCFWS( scursor, send, isCRLF );
1696  if ( parseDigits( scursor, send, d->lines ) == 0 ) {
1697  clear();
1698  return false;
1699  }
1700  return true;
1701 }
1702 
1703 //-----</Lines>--------------------------------
1704 
1705 //-----<Content-Type>--------------------------
1706 
1707 //@cond PRIVATE
1708 kmime_mk_trivial_ctor_with_name_and_dptr( ContentType, Generics::Parametrized,
1709  Content-Type )
1710 //@endcond
1711 
1712 bool ContentType::isEmpty() const
1713 {
1714  return d_func()->mimeType.isEmpty();
1715 }
1716 
1717 void ContentType::clear()
1718 {
1719  Q_D(ContentType);
1720  d->category = CCsingle;
1721  d->mimeType.clear();
1722  Parametrized::clear();
1723 }
1724 
1725 QByteArray ContentType::as7BitString( bool withHeaderType ) const
1726 {
1727  if ( isEmpty() ) {
1728  return QByteArray();
1729  }
1730 
1731  QByteArray rv;
1732  if ( withHeaderType ) {
1733  rv += typeIntro();
1734  }
1735 
1736  rv += mimeType();
1737  if ( !Parametrized::isEmpty() ) {
1738  rv += "; " + Parametrized::as7BitString( false );
1739  }
1740 
1741  return rv;
1742 }
1743 
1744 QByteArray ContentType::mimeType() const
1745 {
1746  Q_D(const ContentType);
1747  return d->mimeType;
1748 }
1749 
1750 QByteArray ContentType::mediaType() const
1751 {
1752  Q_D(const ContentType);
1753  const int pos = d->mimeType.indexOf( '/' );
1754  if ( pos < 0 )
1755  return d->mimeType;
1756  else
1757  return d->mimeType.left( pos );
1758 }
1759 
1760 QByteArray 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 void ContentType::setMimeType( const QByteArray &mimeType )
1771 {
1772  Q_D(ContentType);
1773  d->mimeType = mimeType;
1774  Parametrized::clear();
1775 
1776  if ( isMultipart() ) {
1777  d->category = CCcontainer;
1778  } else {
1779  d->category = CCsingle;
1780  }
1781 }
1782 
1783 bool ContentType::isMediatype( const char *mediatype ) const
1784 {
1785  Q_D(const ContentType);
1786  const int len = strlen( mediatype );
1787  return qstrnicmp( d->mimeType.constData(), mediatype, len ) == 0 && (d->mimeType.at(len) == '/' || d->mimeType.size() == len);
1788 }
1789 
1790 bool ContentType::isSubtype( const char *subtype ) const
1791 {
1792  Q_D(const ContentType);
1793  const int pos = d->mimeType.indexOf( '/' );
1794  if ( pos < 0 )
1795  return false;
1796  const int len = strlen( subtype );
1797  return qstrnicmp( d->mimeType.constData() + pos + 1, subtype, len ) == 0 && d->mimeType.size() == pos + len + 1;
1798 }
1799 
1800 bool ContentType::isText() const
1801 {
1802  return ( isMediatype( "text" ) || isEmpty() );
1803 }
1804 
1805 bool ContentType::isPlainText() const
1806 {
1807  return ( qstricmp( d_func()->mimeType.constData(), "text/plain" ) == 0 || isEmpty() );
1808 }
1809 
1810 bool ContentType::isHTMLText() const
1811 {
1812  return qstricmp( d_func()->mimeType.constData(), "text/html" ) == 0;
1813 }
1814 
1815 bool ContentType::isImage() const
1816 {
1817  return isMediatype( "image" );
1818 }
1819 
1820 bool ContentType::isMultipart() const
1821 {
1822  return isMediatype( "multipart" );
1823 }
1824 
1825 bool ContentType::isPartial() const
1826 {
1827  return qstricmp( d_func()->mimeType.constData(), "message/partial" ) == 0;
1828 }
1829 
1830 QByteArray ContentType::charset() const
1831 {
1832  QByteArray ret = parameter( QLatin1String( "charset" ) ).toLatin1();
1833  if ( ret.isEmpty() || forceDefaultCharset() ) {
1834  //return the default-charset if necessary
1835  ret = defaultCharset();
1836  }
1837  return ret;
1838 }
1839 
1840 void ContentType::setCharset( const QByteArray &s )
1841 {
1842  setParameter( QLatin1String( "charset" ), QString::fromLatin1( s ) );
1843 }
1844 
1845 QByteArray ContentType::boundary() const
1846 {
1847  return parameter( QLatin1String( "boundary" ) ).toLatin1();
1848 }
1849 
1850 void ContentType::setBoundary( const QByteArray &s )
1851 {
1852  setParameter( QLatin1String( "boundary" ), QString::fromLatin1( s ) );
1853 }
1854 
1855 QString ContentType::name() const
1856 {
1857  return parameter( QLatin1String( "name" ) );
1858 }
1859 
1860 void ContentType::setName( const QString &s, const QByteArray &cs )
1861 {
1862  Q_D(ContentType);
1863  d->encCS = cs;
1864  setParameter( QLatin1String( "name" ), s );
1865 }
1866 
1867 QByteArray ContentType::id() const
1868 {
1869  return parameter( QLatin1String( "id" ) ).toLatin1();
1870 }
1871 
1872 void ContentType::setId( const QByteArray &s )
1873 {
1874  setParameter( QLatin1String( "id" ), QString::fromLatin1( s ) );
1875 }
1876 
1877 int ContentType::partialNumber() const
1878 {
1879  QByteArray p = parameter( QLatin1String( "number" ) ).toLatin1();
1880  if ( !p.isEmpty() ) {
1881  return p.toInt();
1882  } else {
1883  return -1;
1884  }
1885 }
1886 
1887 int ContentType::partialCount() const
1888 {
1889  QByteArray p = parameter( QLatin1String( "total" ) ).toLatin1();
1890  if ( !p.isEmpty() ) {
1891  return p.toInt();
1892  } else {
1893  return -1;
1894  }
1895 }
1896 
1897 contentCategory ContentType::category() const
1898 {
1899  return d_func()->category;
1900 }
1901 
1902 void ContentType::setCategory( contentCategory c )
1903 {
1904  Q_D(ContentType);
1905  d->category = c;
1906 }
1907 
1908 void ContentType::setPartialParams( int total, int number )
1909 {
1910  setParameter( QLatin1String( "number" ), QString::number( number ) );
1911  setParameter( QLatin1String( "total" ), QString::number( total ) );
1912 }
1913 
1914 bool ContentType::parse( const char* &scursor, const char * const send,
1915  bool isCRLF )
1916 {
1917  Q_D(ContentType);
1918  // content-type: type "/" subtype *(";" parameter)
1919 
1920  clear();
1921  eatCFWS( scursor, send, isCRLF );
1922  if ( scursor == send ) {
1923  return false; // empty header
1924  }
1925 
1926  // type
1927  QPair<const char*,int> maybeMimeType;
1928  if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) {
1929  return false;
1930  }
1931 
1932  // subtype
1933  eatCFWS( scursor, send, isCRLF );
1934  if ( scursor == send || *scursor != '/' ) {
1935  return false;
1936  }
1937  scursor++;
1938  eatCFWS( scursor, send, isCRLF );
1939  if ( scursor == send ) {
1940  return false;
1941  }
1942 
1943  QPair<const char*,int> maybeSubType;
1944  if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) {
1945  return false;
1946  }
1947 
1948  d->mimeType.reserve( maybeMimeType.second + maybeSubType.second + 1 );
1949  d->mimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower()
1950  + '/' + QByteArray( maybeSubType.first, maybeSubType.second ).toLower();
1951 
1952  // parameter list
1953  eatCFWS( scursor, send, isCRLF );
1954  if ( scursor == send ) {
1955  goto success; // no parameters
1956  }
1957 
1958  if ( *scursor != ';' ) {
1959  return false;
1960  }
1961  scursor++;
1962 
1963  if ( !Parametrized::parse( scursor, send, isCRLF ) ) {
1964  return false;
1965  }
1966 
1967  // adjust category
1968 success:
1969  if ( isMultipart() ) {
1970  d->category = CCcontainer;
1971  } else {
1972  d->category = CCsingle;
1973  }
1974  return true;
1975 }
1976 
1977 //-----</Content-Type>-------------------------
1978 
1979 //-----<ContentID>----------------------
1980 
1981 kmime_mk_trivial_ctor_with_name_and_dptr( ContentID, SingleIdent, Content-ID )
1982 kmime_mk_dptr_ctor( ContentID, SingleIdent )
1983 
1984 bool ContentID::parse( const char* &scursor, const char *const send, bool isCRLF )
1985 {
1986  Q_D ( ContentID );
1987  // Content-id := "<" contentid ">"
1988  // contentid := now whitespaces
1989 
1990  const char* origscursor = scursor;
1991  if ( !SingleIdent::parse ( scursor, send, isCRLF ) )
1992  {
1993  scursor = origscursor;
1994  d->msgIdList.clear();
1995  d->cachedIdentifier.clear();
1996 
1997  while ( scursor != send )
1998  {
1999  eatCFWS ( scursor, send, isCRLF );
2000  // empty entry ending the list: OK.
2001  if ( scursor == send )
2002  {
2003  return true;
2004  }
2005  // empty entry: ignore.
2006  if ( *scursor == ',' )
2007  {
2008  scursor++;
2009  continue;
2010  }
2011 
2012  AddrSpec maybeContentId;
2013  // Almost parseAngleAddr
2014  if ( scursor == send || *scursor != '<' )
2015  {
2016  return false;
2017  }
2018  scursor++; // eat '<'
2019 
2020  eatCFWS ( scursor, send, isCRLF );
2021  if ( scursor == send )
2022  {
2023  return false;
2024  }
2025 
2026  // Save chars untill '>''
2027  QString result;
2028  if( !parseDotAtom(scursor, send, result, false) ) {
2029  return false;
2030  }
2031 
2032  eatCFWS ( scursor, send, isCRLF );
2033  if ( scursor == send || *scursor != '>' )
2034  {
2035  return false;
2036  }
2037  scursor++;
2038  // /Almost parseAngleAddr
2039 
2040  maybeContentId.localPart = result;
2041  d->msgIdList.append ( maybeContentId );
2042 
2043  eatCFWS ( scursor, send, isCRLF );
2044  // header end ending the list: OK.
2045  if ( scursor == send )
2046  {
2047  return true;
2048  }
2049  // regular item separator: eat it.
2050  if ( *scursor == ',' )
2051  {
2052  scursor++;
2053  }
2054  }
2055  return true;
2056  }
2057  else
2058  {
2059  return true;
2060  }
2061 }
2062 
2063 //-----</ContentID>----------------------
2064 
2065 //-----<ContentTransferEncoding>----------------------------
2066 
2067 //@cond PRIVATE
2068 kmime_mk_trivial_ctor_with_name_and_dptr( ContentTransferEncoding,
2069  Generics::Token, Content-Transfer-Encoding )
2070 //@endcond
2071 
2072 typedef struct { const char *s; int e; } encTableType;
2073 
2074 static const encTableType encTable[] =
2075 {
2076  { "7Bit", CE7Bit },
2077  { "8Bit", CE8Bit },
2078  { "quoted-printable", CEquPr },
2079  { "base64", CEbase64 },
2080  { "x-uuencode", CEuuenc },
2081  { "binary", CEbinary },
2082  { 0, 0}
2083 };
2084 
2085 void ContentTransferEncoding::clear()
2086 {
2087  Q_D(ContentTransferEncoding);
2088  d->decoded = true;
2089  d->cte = CE7Bit;
2090  Token::clear();
2091 }
2092 
2093 contentEncoding ContentTransferEncoding::encoding() const
2094 {
2095  return d_func()->cte;
2096 }
2097 
2098 void ContentTransferEncoding::setEncoding( contentEncoding e )
2099 {
2100  Q_D(ContentTransferEncoding);
2101  d->cte = e;
2102 
2103  for ( int i = 0; encTable[i].s != 0; ++i ) {
2104  if ( d->cte == encTable[i].e ) {
2105  setToken( encTable[i].s );
2106  break;
2107  }
2108  }
2109 }
2110 
2111 bool ContentTransferEncoding::decoded() const
2112 {
2113  return d_func()->decoded;
2114 }
2115 
2116 void ContentTransferEncoding::setDecoded( bool decoded )
2117 {
2118  Q_D(ContentTransferEncoding);
2119  d->decoded = decoded;
2120 }
2121 
2122 bool ContentTransferEncoding::needToEncode() const
2123 {
2124  const Q_D(ContentTransferEncoding);
2125  return d->decoded && (d->cte == CEquPr || d->cte == CEbase64);
2126 }
2127 
2128 bool ContentTransferEncoding::parse( const char *& scursor,
2129  const char * const send, bool isCRLF )
2130 {
2131  Q_D(ContentTransferEncoding);
2132  clear();
2133  if ( !Token::parse( scursor, send, isCRLF ) ) {
2134  return false;
2135  }
2136 
2137  // TODO: error handling in case of an unknown encoding?
2138  for ( int i = 0; encTable[i].s != 0; ++i ) {
2139  if ( qstricmp( token().constData(), encTable[i].s ) == 0 ) {
2140  d->cte = ( contentEncoding )encTable[i].e;
2141  break;
2142  }
2143  }
2144  d->decoded = ( d->cte == CE7Bit || d->cte == CE8Bit );
2145  return true;
2146 }
2147 
2148 //-----</ContentTransferEncoding>---------------------------
2149 
2150 //-----<ContentDisposition>--------------------------
2151 
2152 //@cond PRIVATE
2153 kmime_mk_trivial_ctor_with_name_and_dptr( ContentDisposition,
2154  Generics::Parametrized, Content-Disposition )
2155 //@endcond
2156 
2157 QByteArray ContentDisposition::as7BitString( bool withHeaderType ) const
2158 {
2159  if ( isEmpty() ) {
2160  return QByteArray();
2161  }
2162 
2163  QByteArray rv;
2164  if ( withHeaderType ) {
2165  rv += typeIntro();
2166  }
2167 
2168  if ( d_func()->disposition == CDattachment ) {
2169  rv += "attachment";
2170  } else if ( d_func()->disposition == CDinline ) {
2171  rv += "inline";
2172  } else {
2173  return QByteArray();
2174  }
2175 
2176  if ( !Parametrized::isEmpty() ) {
2177  rv += "; " + Parametrized::as7BitString( false );
2178  }
2179 
2180  return rv;
2181 }
2182 
2183 bool ContentDisposition::isEmpty() const
2184 {
2185  return d_func()->disposition == CDInvalid;
2186 }
2187 
2188 void ContentDisposition::clear()
2189 {
2190  Q_D(ContentDisposition);
2191  d->disposition = CDInvalid;
2192  Parametrized::clear();
2193 }
2194 
2195 contentDisposition ContentDisposition::disposition() const
2196 {
2197  return d_func()->disposition;
2198 }
2199 
2200 void ContentDisposition::setDisposition( contentDisposition disp )
2201 {
2202  Q_D(ContentDisposition);
2203  d->disposition = disp;
2204 }
2205 
2206 QString KMime::Headers::ContentDisposition::filename() const
2207 {
2208  return parameter( QLatin1String( "filename" ) );
2209 }
2210 
2211 void ContentDisposition::setFilename( const QString &filename )
2212 {
2213  setParameter( QLatin1String( "filename" ), filename );
2214 }
2215 
2216 bool ContentDisposition::parse( const char *& scursor, const char * const send,
2217  bool isCRLF )
2218 {
2219  Q_D(ContentDisposition);
2220  clear();
2221 
2222  // token
2223  QByteArray token;
2224  eatCFWS( scursor, send, isCRLF );
2225  if ( scursor == send ) {
2226  return false;
2227  }
2228 
2229  QPair<const char*,int> maybeToken;
2230  if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) {
2231  return false;
2232  }
2233 
2234  token = QByteArray( maybeToken.first, maybeToken.second ).toLower();
2235  if ( token == "inline" ) {
2236  d->disposition = CDinline;
2237  } else if ( token == "attachment" ) {
2238  d->disposition = CDattachment;
2239  } else {
2240  return false;
2241  }
2242 
2243  // parameter list
2244  eatCFWS( scursor, send, isCRLF );
2245  if ( scursor == send ) {
2246  return true; // no parameters
2247  }
2248 
2249  if ( *scursor != ';' ) {
2250  return false;
2251  }
2252  scursor++;
2253 
2254  return Parametrized::parse( scursor, send, isCRLF );
2255 }
2256 
2257 //-----</ContentDisposition>-------------------------
2258 
2259 //@cond PRIVATE
2260 kmime_mk_trivial_ctor_with_name( Subject, Generics::Unstructured, Subject )
2261 //@endcond
2262 
2263 bool Subject::isReply() const
2264 {
2265  return asUnicodeString().indexOf( QLatin1String( "Re:" ), 0, Qt::CaseInsensitive ) == 0;
2266 }
2267 
2268 Base* createHeader( const QByteArray& type )
2269 {
2270  return HeaderFactory::self()->createHeader( type );
2271 }
2272 
2273 
2274 //@cond PRIVATE
2275 kmime_mk_trivial_ctor_with_name( ContentDescription,
2276  Generics::Unstructured, Content-Description )
2277 kmime_mk_trivial_ctor_with_name( ContentLocation,
2278  Generics::Unstructured, Content-Location )
2279 kmime_mk_trivial_ctor_with_name( From, Generics::MailboxList, From )
2280 kmime_mk_trivial_ctor_with_name( Sender, Generics::SingleMailbox, Sender )
2281 kmime_mk_trivial_ctor_with_name( To, Generics::AddressList, To )
2282 kmime_mk_trivial_ctor_with_name( Cc, Generics::AddressList, Cc )
2283 kmime_mk_trivial_ctor_with_name( Bcc, Generics::AddressList, Bcc )
2284 kmime_mk_trivial_ctor_with_name( ReplyTo, Generics::AddressList, Reply-To )
2285 kmime_mk_trivial_ctor_with_name( Keywords, Generics::PhraseList, Keywords )
2286 kmime_mk_trivial_ctor_with_name( MIMEVersion, Generics::DotAtom, MIME-Version )
2287 kmime_mk_trivial_ctor_with_name( Supersedes, Generics::SingleIdent, Supersedes )
2288 kmime_mk_trivial_ctor_with_name( InReplyTo, Generics::Ident, In-Reply-To )
2289 kmime_mk_trivial_ctor_with_name( References, Generics::Ident, References )
2290 kmime_mk_trivial_ctor_with_name( Organization, Generics::Unstructured, Organization )
2291 kmime_mk_trivial_ctor_with_name( UserAgent, Generics::Unstructured, User-Agent )
2292 //@endcond
2293 
2294 } // namespace Headers
2295 
2296 } // namespace KMime
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Dec 10 2012 13:47:15 by doxygen 1.8.1.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KMIME Library

Skip menu "KMIME Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.9.4 API Reference

Skip menu "kdepimlibs-4.9.4 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal