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

KDE's Doxygen guidelines are available online.