KMime

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

KDE's Doxygen guidelines are available online.