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

KDE's Doxygen guidelines are available online.