KMime

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

KDE's Doxygen guidelines are available online.