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

KDE's Doxygen guidelines are available online.