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

KDE's Doxygen guidelines are available online.