KMime

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

KDE's Doxygen guidelines are available online.