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

KDE's Doxygen guidelines are available online.