File indexing completed on 2024-04-21 16:06:18
0001 /* -*- c++ -*- 0002 kmime_headers.cpp 0003 0004 KMime, the KDE Internet mail/usenet news message library. 0005 SPDX-FileCopyrightText: 2001-2002 the KMime authors. 0006 See file AUTHORS for details 0007 SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 /** 0012 @file 0013 This file is part of the API for handling @ref MIME data and 0014 defines the various header classes: 0015 - header's base class defining the common interface 0016 - generic base classes for different types of fields 0017 - incompatible, Structured-based field classes 0018 - compatible, Unstructured-based field classes 0019 0020 @brief 0021 Defines the various headers classes. 0022 0023 @authors the KMime authors (see AUTHORS file), 0024 Volker Krause \<vkrause@kde.org\> 0025 */ 0026 0027 #include "kmime_headers.h" 0028 #include "kmime_headers_p.h" 0029 0030 #include "kmime_util.h" 0031 #include "kmime_util_p.h" 0032 #include "kmime_codecs_p.h" 0033 #include "kmime_content.h" 0034 #include "kmime_headerfactory_p.h" 0035 #include "kmime_debug.h" 0036 #include "kmime_warning_p.h" 0037 0038 #include <KCodecs> 0039 0040 #include <cassert> 0041 #include <cctype> 0042 0043 // macro to generate a default constructor implementation 0044 #define kmime_mk_trivial_ctor( subclass, baseclass ) \ 0045 subclass::subclass() : baseclass() \ 0046 { \ 0047 } \ 0048 \ 0049 subclass::~subclass() {} 0050 0051 // end kmime_mk_trivial_ctor 0052 0053 #define kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \ 0054 subclass::subclass() : baseclass( new subclass##Private ) \ 0055 { \ 0056 } \ 0057 \ 0058 subclass::~subclass() { \ 0059 Q_D(subclass); \ 0060 delete d; /* see comment above the BasePrivate class */ \ 0061 d_ptr = nullptr; \ 0062 } 0063 0064 // end kmime_mk_trivial_ctor_with_dptr 0065 0066 #define kmime_mk_trivial_ctor_with_name( subclass, baseclass, name ) \ 0067 kmime_mk_trivial_ctor( subclass, baseclass ) \ 0068 \ 0069 const char *subclass::type() const \ 0070 { \ 0071 return staticType(); \ 0072 } \ 0073 const char *subclass::staticType() { return #name; } 0074 0075 #define kmime_mk_trivial_ctor_with_name_and_dptr( subclass, baseclass, name ) \ 0076 kmime_mk_trivial_ctor_with_dptr( subclass, baseclass ) \ 0077 const char *subclass::type() const { return staticType(); } \ 0078 const char *subclass::staticType() { return #name; } 0079 0080 #define kmime_mk_dptr_ctor( subclass, baseclass ) \ 0081 subclass::subclass( subclass##Private *d ) : baseclass( d ) {} 0082 0083 using namespace KMime; 0084 using namespace KMime::Headers; 0085 using namespace KMime::Types; 0086 using namespace KMime::HeaderParsing; 0087 0088 namespace KMime 0089 { 0090 namespace Headers 0091 { 0092 //-----<Base>---------------------------------- 0093 Base::Base() : d_ptr(new BasePrivate) 0094 { 0095 } 0096 0097 Base::Base(BasePrivate *dd) : 0098 d_ptr(dd) 0099 { 0100 } 0101 0102 Base::~Base() 0103 { 0104 delete d_ptr; 0105 d_ptr = nullptr; 0106 } 0107 0108 void Base::from7BitString(const char *s, size_t len) 0109 { 0110 from7BitString(QByteArray::fromRawData(s, len)); 0111 } 0112 0113 QByteArray Base::rfc2047Charset() const 0114 { 0115 if (d_ptr->encCS.isEmpty()) { 0116 return Content::defaultCharset(); 0117 } else { 0118 return d_ptr->encCS; 0119 } 0120 } 0121 0122 void Base::setRFC2047Charset(const QByteArray &cs) 0123 { 0124 d_ptr->encCS = cachedCharset(cs); 0125 } 0126 0127 const char *Base::type() const 0128 { 0129 return ""; 0130 } 0131 0132 bool Base::is(const char *t) const 0133 { 0134 return qstricmp(t, type()) == 0; 0135 } 0136 0137 bool Base::isMimeHeader() const 0138 { 0139 return qstrnicmp(type(), "Content-", 8) == 0; 0140 } 0141 0142 QByteArray Base::typeIntro() const 0143 { 0144 return QByteArray(type()) + ": "; 0145 } 0146 0147 //-----</Base>--------------------------------- 0148 0149 namespace Generics 0150 { 0151 0152 //-----<Unstructured>------------------------- 0153 0154 //@cond PRIVATE 0155 kmime_mk_dptr_ctor(Unstructured, Base) 0156 //@endcond 0157 0158 Unstructured::Unstructured() : Base(new UnstructuredPrivate) 0159 { 0160 } 0161 0162 Unstructured::~Unstructured() 0163 { 0164 Q_D(Unstructured); 0165 delete d; 0166 d_ptr = nullptr; 0167 } 0168 0169 void Unstructured::from7BitString(const QByteArray &s) 0170 { 0171 Q_D(Unstructured); 0172 d->decoded = KCodecs::decodeRFC2047String(s, &d->encCS, Content::defaultCharset()); 0173 } 0174 0175 QByteArray Unstructured::as7BitString(bool withHeaderType) const 0176 { 0177 const Q_D(Unstructured); 0178 QByteArray result; 0179 if (withHeaderType) { 0180 result = typeIntro(); 0181 } 0182 result += encodeRFC2047String(d->decoded, d->encCS) ; 0183 0184 return result; 0185 } 0186 0187 void Unstructured::fromUnicodeString(const QString &s, const QByteArray &b) 0188 { 0189 Q_D(Unstructured); 0190 d->decoded = s; 0191 d->encCS = cachedCharset(b); 0192 } 0193 0194 QString Unstructured::asUnicodeString() const 0195 { 0196 return d_func()->decoded; 0197 } 0198 0199 void Unstructured::clear() 0200 { 0201 Q_D(Unstructured); 0202 d->decoded.truncate(0); 0203 } 0204 0205 bool Unstructured::isEmpty() const 0206 { 0207 return d_func()->decoded.isEmpty(); 0208 } 0209 0210 //-----</Unstructured>------------------------- 0211 0212 //-----<Structured>------------------------- 0213 0214 Structured::Structured() : Base(new StructuredPrivate) 0215 { 0216 } 0217 0218 kmime_mk_dptr_ctor(Structured, Base) 0219 0220 Structured::~Structured() 0221 { 0222 Q_D(Structured); 0223 delete d; 0224 d_ptr = nullptr; 0225 } 0226 0227 0228 void Structured::from7BitString(const char *s, size_t len) 0229 { 0230 Q_D(Structured); 0231 if (d->encCS.isEmpty()) { 0232 d->encCS = Content::defaultCharset(); 0233 } 0234 parse(s, s + len); 0235 } 0236 0237 void Structured::from7BitString(const QByteArray &s) 0238 { 0239 #if 0 0240 Q_D(Structured); 0241 //Bug about mailto with space which are replaced by "_" so it failed to parse 0242 //=> we reconvert to correct encoding as RFC2047 0243 const QString str = KCodecs::decodeRFC2047String(s, &d->encCS, Content::defaultCharset()); 0244 const QByteArray ba = KCodecs::encodeRFC2047String(str, d->encCS); 0245 from7BitString(ba.constData(), ba.length()); 0246 #else 0247 from7BitString(s.constData(), s.length()); 0248 #endif 0249 } 0250 0251 QString Structured::asUnicodeString() const 0252 { 0253 return QString::fromLatin1(as7BitString(false)); 0254 } 0255 0256 void Structured::fromUnicodeString(const QString &s, const QByteArray &b) 0257 { 0258 Q_D(Structured); 0259 d->encCS = cachedCharset(b); 0260 from7BitString(s.toLatin1()); 0261 } 0262 0263 //-----</Structured>------------------------- 0264 0265 //-----<Address>------------------------- 0266 0267 Address::Address() : Structured(new AddressPrivate) 0268 { 0269 } 0270 0271 kmime_mk_dptr_ctor(Address, Structured) 0272 0273 Address::~Address() = default; 0274 0275 // helper method used in AddressList and MailboxList 0276 static bool stringToMailbox(const QByteArray &address, 0277 const QString &displayName, Types::Mailbox &mbox) 0278 { 0279 Types::AddrSpec addrSpec; 0280 mbox.setName(displayName); 0281 const char *cursor = address.constData(); 0282 if (!parseAngleAddr(cursor, cursor + address.length(), addrSpec)) { 0283 if (!parseAddrSpec(cursor, cursor + address.length(), addrSpec)) { 0284 qCWarning(KMIME_LOG) << "stringToMailbox: Invalid address"; 0285 return false; 0286 } 0287 } 0288 mbox.setAddress(addrSpec); 0289 return true; 0290 } 0291 0292 //-----</Address>------------------------- 0293 0294 //-----<MailboxList>------------------------- 0295 0296 kmime_mk_trivial_ctor_with_dptr(MailboxList, Address) 0297 kmime_mk_dptr_ctor(MailboxList, Address) 0298 0299 QByteArray MailboxList::as7BitString(bool withHeaderType) const 0300 { 0301 const Q_D(MailboxList); 0302 if (isEmpty()) { 0303 return {}; 0304 } 0305 0306 QByteArray rv; 0307 if (withHeaderType) { 0308 rv = typeIntro(); 0309 } 0310 for (const Types::Mailbox &mbox : std::as_const(d->mailboxList)) { 0311 rv += mbox.as7BitString(d->encCS); 0312 rv += ", "; 0313 } 0314 rv.resize(rv.length() - 2); 0315 return rv; 0316 } 0317 0318 void MailboxList::fromUnicodeString(const QString &s, const QByteArray &b) 0319 { 0320 Q_D(MailboxList); 0321 d->encCS = cachedCharset(b); 0322 from7BitString(encodeRFC2047Sentence(s, b)); 0323 } 0324 0325 QString MailboxList::asUnicodeString() const 0326 { 0327 Q_D(const MailboxList); 0328 return Mailbox::listToUnicodeString(d->mailboxList); 0329 } 0330 0331 void MailboxList::clear() 0332 { 0333 Q_D(MailboxList); 0334 d->mailboxList.clear(); 0335 } 0336 0337 bool MailboxList::isEmpty() const 0338 { 0339 return d_func()->mailboxList.isEmpty(); 0340 } 0341 0342 void MailboxList::addAddress(const Types::Mailbox &mbox) 0343 { 0344 Q_D(MailboxList); 0345 d->mailboxList.append(mbox); 0346 } 0347 0348 void MailboxList::addAddress(const QByteArray &address, 0349 const QString &displayName) 0350 { 0351 Q_D(MailboxList); 0352 Types::Mailbox mbox; 0353 if (stringToMailbox(address, displayName, mbox)) { 0354 d->mailboxList.append(mbox); 0355 } 0356 } 0357 0358 QList<QByteArray> MailboxList::addresses() const { 0359 QList<QByteArray> rv; 0360 rv.reserve(d_func()->mailboxList.count()); 0361 const auto mailboxList = d_func()->mailboxList; 0362 for (const Types::Mailbox &mbox : mailboxList) { 0363 rv.append(mbox.address()); 0364 } 0365 return rv; 0366 } 0367 0368 QStringList MailboxList::displayNames() const 0369 { 0370 Q_D(const MailboxList); 0371 QStringList rv; 0372 rv.reserve(d->mailboxList.count()); 0373 for (const Types::Mailbox &mbox : std::as_const(d->mailboxList)) { 0374 if (mbox.hasName()) { 0375 rv.append(mbox.name()); 0376 } else { 0377 rv.append(QString::fromLatin1(mbox.address())); 0378 } 0379 } 0380 return rv; 0381 } 0382 0383 QString MailboxList::displayString() const 0384 { 0385 Q_D(const MailboxList); 0386 if (d->mailboxList.size() == 1) { // fast-path to avoid temporary QStringList in the common case of just one From address 0387 const auto& mbox = d->mailboxList.at(0); 0388 if (mbox.hasName()) { 0389 return mbox.name(); 0390 } else { 0391 return QString::fromLatin1(mbox.address()); 0392 } 0393 } 0394 return displayNames().join(QLatin1StringView(", ")); 0395 } 0396 0397 Types::Mailbox::List MailboxList::mailboxes() const 0398 { 0399 return d_func()->mailboxList; 0400 } 0401 0402 bool MailboxList::parse(const char *&scursor, const char *const send, 0403 bool isCRLF) 0404 { 0405 Q_D(MailboxList); 0406 // examples: 0407 // from := "From:" mailbox-list CRLF 0408 // sender := "Sender:" mailbox CRLF 0409 0410 // parse an address-list: 0411 QList<Types::Address> maybeAddressList; 0412 if (!parseAddressList(scursor, send, maybeAddressList, isCRLF)) { 0413 return false; 0414 } 0415 0416 d->mailboxList.clear(); 0417 d->mailboxList.reserve(maybeAddressList.count()); 0418 0419 // extract the mailboxes and complain if there are groups: 0420 for (const auto &it : std::as_const(maybeAddressList)) { 0421 if (!(it).displayName.isEmpty()) { 0422 KMIME_WARN << "mailbox groups in header disallowing them! Name: \"" 0423 << (it).displayName << "\"" 0424 << Qt::endl 0425 ; 0426 } 0427 d->mailboxList += (it).mailboxList; 0428 } 0429 return true; 0430 } 0431 0432 //-----</MailboxList>------------------------- 0433 0434 //-----<SingleMailbox>------------------------- 0435 0436 //@cond PRIVATE 0437 kmime_mk_trivial_ctor_with_dptr(SingleMailbox, MailboxList) 0438 //@endcond 0439 0440 bool SingleMailbox::parse(const char *&scursor, const char *const send, 0441 bool isCRLF) 0442 { 0443 Q_D(MailboxList); 0444 if (!MailboxList::parse(scursor, send, isCRLF)) { 0445 return false; 0446 } 0447 0448 if (d->mailboxList.count() > 1) { 0449 KMIME_WARN << "multiple mailboxes in header allowing only a single one!" 0450 << Qt::endl; 0451 } 0452 return true; 0453 } 0454 0455 //-----</SingleMailbox>------------------------- 0456 0457 //-----<AddressList>------------------------- 0458 0459 //@cond PRIVATE 0460 kmime_mk_trivial_ctor_with_dptr(AddressList, Address) 0461 kmime_mk_dptr_ctor(AddressList, Address) 0462 //@endcond 0463 0464 QByteArray AddressList::as7BitString(bool withHeaderType) const 0465 { 0466 const Q_D(AddressList); 0467 if (d->addressList.isEmpty()) { 0468 return {}; 0469 } 0470 0471 QByteArray rv; 0472 if (withHeaderType) { 0473 rv = typeIntro(); 0474 } 0475 for (const Types::Address &addr : std::as_const(d->addressList)) { 0476 const auto mailBoxList = addr.mailboxList; 0477 for (const Types::Mailbox &mbox : mailBoxList) { 0478 rv += mbox.as7BitString(d->encCS); 0479 rv += ", "; 0480 } 0481 } 0482 rv.resize(rv.length() - 2); 0483 return rv; 0484 } 0485 0486 void AddressList::fromUnicodeString(const QString &s, const QByteArray &b) 0487 { 0488 Q_D(AddressList); 0489 d->encCS = cachedCharset(b); 0490 from7BitString(encodeRFC2047Sentence(s, b)); 0491 } 0492 0493 QString AddressList::asUnicodeString() const 0494 { 0495 Q_D(const AddressList); 0496 QStringList rv; 0497 for (const Types::Address &addr : std::as_const(d->addressList)) { 0498 rv.reserve(rv.size() + addr.mailboxList.size()); 0499 const auto mailboxList = addr.mailboxList; 0500 for (const Types::Mailbox &mbox : mailboxList) { 0501 rv.append(mbox.prettyAddress()); 0502 } 0503 } 0504 return rv.join(QLatin1StringView(", ")); 0505 } 0506 0507 void AddressList::clear() 0508 { 0509 Q_D(AddressList); 0510 d->addressList.clear(); 0511 } 0512 0513 bool AddressList::isEmpty() const 0514 { 0515 return d_func()->addressList.isEmpty(); 0516 } 0517 0518 void AddressList::addAddress(const Types::Mailbox &mbox) 0519 { 0520 Q_D(AddressList); 0521 Types::Address addr; 0522 addr.mailboxList.append(mbox); 0523 d->addressList.append(addr); 0524 } 0525 0526 void AddressList::addAddress(const QByteArray &address, 0527 const QString &displayName) 0528 { 0529 Q_D(AddressList); 0530 Types::Address addr; 0531 Types::Mailbox mbox; 0532 if (stringToMailbox(address, displayName, mbox)) { 0533 addr.mailboxList.append(mbox); 0534 d->addressList.append(addr); 0535 } 0536 } 0537 0538 QList<QByteArray> AddressList::addresses() const { 0539 QList<QByteArray> rv; 0540 const auto addressList = d_func()->addressList; 0541 for (const Types::Address &addr : addressList) { 0542 const auto mailboxList = addr.mailboxList; 0543 for (const Types::Mailbox &mbox : mailboxList) { 0544 rv.append(mbox.address()); 0545 } 0546 } 0547 return rv; 0548 } 0549 0550 QStringList AddressList::displayNames() const 0551 { 0552 Q_D(const AddressList); 0553 QStringList rv; 0554 for (const Types::Address &addr : std::as_const(d->addressList)) { 0555 const auto mailboxList = addr.mailboxList; 0556 for (const Types::Mailbox &mbox : mailboxList) { 0557 if (mbox.hasName()) { 0558 rv.append(mbox.name()); 0559 } else { 0560 rv.append(QString::fromLatin1(mbox.address())); 0561 } 0562 } 0563 } 0564 return rv; 0565 } 0566 0567 QString AddressList::displayString() const 0568 { 0569 // optimize for single entry and avoid creation of the QStringList in that case? 0570 return displayNames().join(QLatin1StringView(", ")); 0571 } 0572 0573 Types::Mailbox::List AddressList::mailboxes() const 0574 { 0575 Types::Mailbox::List rv; 0576 const auto addressList = d_func()->addressList; 0577 for (const Types::Address &addr : addressList) { 0578 const auto mailboxList = addr.mailboxList; 0579 for (const Types::Mailbox &mbox : mailboxList) { 0580 rv.append(mbox); 0581 } 0582 } 0583 return rv; 0584 } 0585 0586 bool AddressList::parse(const char *&scursor, const char *const send, 0587 bool isCRLF) 0588 { 0589 Q_D(AddressList); 0590 QList<Types::Address> maybeAddressList; 0591 if (!parseAddressList(scursor, send, maybeAddressList, isCRLF)) { 0592 return false; 0593 } 0594 0595 d->addressList = maybeAddressList; 0596 return true; 0597 } 0598 0599 //-----</AddressList>------------------------- 0600 0601 //-----<Token>------------------------- 0602 0603 //@cond PRIVATE 0604 kmime_mk_trivial_ctor_with_dptr(Token, Structured) 0605 kmime_mk_dptr_ctor(Token, Structured) 0606 //@endcond 0607 0608 QByteArray Token::as7BitString(bool withHeaderType) const 0609 { 0610 if (isEmpty()) { 0611 return {}; 0612 } 0613 if (withHeaderType) { 0614 return typeIntro() + d_func()->token; 0615 } 0616 return d_func()->token; 0617 } 0618 0619 void Token::clear() 0620 { 0621 Q_D(Token); 0622 d->token.clear(); 0623 } 0624 0625 bool Token::isEmpty() const 0626 { 0627 return d_func()->token.isEmpty(); 0628 } 0629 0630 QByteArray Token::token() const 0631 { 0632 return d_func()->token; 0633 } 0634 0635 void Token::setToken(const QByteArray &t) 0636 { 0637 Q_D(Token); 0638 d->token = t; 0639 } 0640 0641 bool Token::parse(const char *&scursor, const char *const send, bool isCRLF) 0642 { 0643 Q_D(Token); 0644 clear(); 0645 eatCFWS(scursor, send, isCRLF); 0646 // must not be empty: 0647 if (scursor == send) { 0648 return false; 0649 } 0650 0651 QPair<const char *, int> maybeToken; 0652 if (!parseToken(scursor, send, maybeToken, ParseTokenNoFlag)) { 0653 return false; 0654 } 0655 d->token = QByteArray(maybeToken.first, maybeToken.second); 0656 0657 // complain if trailing garbage is found: 0658 eatCFWS(scursor, send, isCRLF); 0659 if (scursor != send) { 0660 KMIME_WARN << "trailing garbage after token in header allowing " 0661 "only a single token!" 0662 << Qt::endl; 0663 } 0664 return true; 0665 } 0666 0667 //-----</Token>------------------------- 0668 0669 //-----<PhraseList>------------------------- 0670 0671 //@cond PRIVATE 0672 kmime_mk_trivial_ctor_with_dptr(PhraseList, Structured) 0673 //@endcond 0674 0675 QByteArray PhraseList::as7BitString(bool withHeaderType) const 0676 { 0677 const Q_D(PhraseList); 0678 if (isEmpty()) { 0679 return {}; 0680 } 0681 0682 QByteArray rv; 0683 if (withHeaderType) { 0684 rv = typeIntro(); 0685 } 0686 0687 for (int i = 0; i < d->phraseList.count(); ++i) { 0688 // FIXME: only encode when needed, quote when needed, etc. 0689 rv += encodeRFC2047String(d->phraseList[i], d->encCS, false); 0690 if (i != d->phraseList.count() - 1) { 0691 rv += ", "; 0692 } 0693 } 0694 0695 return rv; 0696 } 0697 0698 QString PhraseList::asUnicodeString() const 0699 { 0700 return d_func()->phraseList.join(QLatin1StringView(", ")); 0701 } 0702 0703 void PhraseList::clear() 0704 { 0705 Q_D(PhraseList); 0706 d->phraseList.clear(); 0707 } 0708 0709 bool PhraseList::isEmpty() const 0710 { 0711 return d_func()->phraseList.isEmpty(); 0712 } 0713 0714 QStringList PhraseList::phrases() const 0715 { 0716 return d_func()->phraseList; 0717 } 0718 0719 bool PhraseList::parse(const char *&scursor, const char *const send, 0720 bool isCRLF) 0721 { 0722 Q_D(PhraseList); 0723 d->phraseList.clear(); 0724 0725 while (scursor != send) { 0726 eatCFWS(scursor, send, isCRLF); 0727 // empty entry ending the list: OK. 0728 if (scursor == send) { 0729 return true; 0730 } 0731 // empty entry: ignore. 0732 if (*scursor == ',') { 0733 scursor++; 0734 continue; 0735 } 0736 0737 QString maybePhrase; 0738 if (!parsePhrase(scursor, send, maybePhrase, isCRLF)) { 0739 return false; 0740 } 0741 d->phraseList.append(maybePhrase); 0742 0743 eatCFWS(scursor, send, isCRLF); 0744 // non-empty entry ending the list: OK. 0745 if (scursor == send) { 0746 return true; 0747 } 0748 // comma separating the phrases: eat. 0749 if (*scursor == ',') { 0750 scursor++; 0751 } 0752 } 0753 return true; 0754 } 0755 0756 //-----</PhraseList>------------------------- 0757 0758 //-----<DotAtom>------------------------- 0759 0760 //@cond PRIVATE 0761 kmime_mk_trivial_ctor_with_dptr(DotAtom, Structured) 0762 //@endcond 0763 0764 QByteArray DotAtom::as7BitString(bool withHeaderType) const 0765 { 0766 if (isEmpty()) { 0767 return {}; 0768 } 0769 0770 QByteArray rv; 0771 if (withHeaderType) { 0772 rv += typeIntro(); 0773 } 0774 0775 rv += d_func()->dotAtom; 0776 return rv; 0777 } 0778 0779 QString DotAtom::asUnicodeString() const 0780 { 0781 return QString::fromLatin1(d_func()->dotAtom); 0782 } 0783 0784 void DotAtom::clear() 0785 { 0786 Q_D(DotAtom); 0787 d->dotAtom.clear(); 0788 } 0789 0790 bool DotAtom::isEmpty() const 0791 { 0792 return d_func()->dotAtom.isEmpty(); 0793 } 0794 0795 bool DotAtom::parse(const char *&scursor, const char *const send, 0796 bool isCRLF) 0797 { 0798 Q_D(DotAtom); 0799 QByteArray maybeDotAtom; 0800 if (!parseDotAtom(scursor, send, maybeDotAtom, isCRLF)) { 0801 return false; 0802 } 0803 0804 d->dotAtom = maybeDotAtom; 0805 0806 eatCFWS(scursor, send, isCRLF); 0807 if (scursor != send) { 0808 KMIME_WARN << "trailing garbage after dot-atom in header allowing " 0809 "only a single dot-atom!" 0810 << Qt::endl; 0811 } 0812 return true; 0813 } 0814 0815 //-----</DotAtom>------------------------- 0816 0817 //-----<Parametrized>------------------------- 0818 0819 //@cond PRIVATE 0820 kmime_mk_trivial_ctor_with_dptr(Parametrized, Structured) 0821 kmime_mk_dptr_ctor(Parametrized, Structured) 0822 //@endcond 0823 0824 QByteArray Parametrized::as7BitString(bool withHeaderType) const 0825 { 0826 const Q_D(Parametrized); 0827 if (isEmpty()) { 0828 return {}; 0829 } 0830 0831 QByteArray rv; 0832 if (withHeaderType) { 0833 rv += typeIntro(); 0834 } 0835 0836 bool first = true; 0837 for (QMap<QString, QString>::ConstIterator it = d->parameterHash.constBegin(); 0838 it != d->parameterHash.constEnd(); ++it) { 0839 if (!first) { 0840 rv += "; "; 0841 } else { 0842 first = false; 0843 } 0844 if (isUsAscii(it.value())) { 0845 rv += it.key().toLatin1() + '='; 0846 QByteArray tmp = it.value().toLatin1(); 0847 addQuotes(tmp, true); // force quoting, eg. for whitespaces in parameter value 0848 rv += tmp; 0849 } else { 0850 rv += it.key().toLatin1() + "*="; 0851 rv += encodeRFC2231String(it.value(), d->encCS); 0852 } 0853 } 0854 0855 return rv; 0856 } 0857 0858 QString Parametrized::parameter(const QString &key) const 0859 { 0860 return d_func()->parameterHash.value(key.toLower()); 0861 } 0862 0863 bool Parametrized::hasParameter(const QString &key) const 0864 { 0865 return d_func()->parameterHash.contains(key.toLower()); 0866 } 0867 0868 void Parametrized::setParameter(const QString &key, const QString &value) 0869 { 0870 Q_D(Parametrized); 0871 d->parameterHash.insert(key.toLower(), value); 0872 } 0873 0874 bool Parametrized::isEmpty() const 0875 { 0876 return d_func()->parameterHash.isEmpty(); 0877 } 0878 0879 void Parametrized::clear() 0880 { 0881 Q_D(Parametrized); 0882 d->parameterHash.clear(); 0883 } 0884 0885 bool Parametrized::parse(const char *&scursor, const char *const send, 0886 bool isCRLF) 0887 { 0888 Q_D(Parametrized); 0889 d->parameterHash.clear(); 0890 QByteArray charset; 0891 if (!parseParameterListWithCharset(scursor, send, d->parameterHash, charset, isCRLF)) { 0892 return false; 0893 } 0894 d->encCS = charset; 0895 return true; 0896 } 0897 0898 //-----</Parametrized>------------------------- 0899 0900 //-----<Ident>------------------------- 0901 0902 //@cond PRIVATE 0903 kmime_mk_trivial_ctor_with_dptr(Ident, Address) 0904 kmime_mk_dptr_ctor(Ident, Address) 0905 //@endcond 0906 0907 QByteArray Ident::as7BitString(bool withHeaderType) const 0908 { 0909 const Q_D(Ident); 0910 if (d->msgIdList.isEmpty()) { 0911 return {}; 0912 } 0913 0914 QByteArray rv; 0915 if (withHeaderType) { 0916 rv = typeIntro(); 0917 } 0918 for (const Types::AddrSpec &addr : std::as_const(d->msgIdList)) { 0919 if (!addr.isEmpty()) { 0920 const QString asString = addr.asString(); 0921 rv += '<'; 0922 if (!asString.isEmpty()) { 0923 rv += asString.toLatin1(); // FIXME: change parsing to use QByteArrays 0924 } 0925 rv += "> "; 0926 } 0927 } 0928 if (!rv.isEmpty()) { 0929 rv.resize(rv.length() - 1); 0930 } 0931 return rv; 0932 } 0933 0934 void Ident::clear() 0935 { 0936 Q_D(Ident); 0937 d->msgIdList.clear(); 0938 d->cachedIdentifier.clear(); 0939 } 0940 0941 bool Ident::isEmpty() const 0942 { 0943 return d_func()->msgIdList.isEmpty(); 0944 } 0945 0946 bool Ident::parse(const char *&scursor, const char *const send, bool isCRLF) 0947 { 0948 Q_D(Ident); 0949 // msg-id := "<" id-left "@" id-right ">" 0950 // id-left := dot-atom-text / no-fold-quote / local-part 0951 // id-right := dot-atom-text / no-fold-literal / domain 0952 // 0953 // equivalent to: 0954 // msg-id := angle-addr 0955 0956 d->msgIdList.clear(); 0957 d->cachedIdentifier.clear(); 0958 0959 while (scursor != send) { 0960 eatCFWS(scursor, send, isCRLF); 0961 // empty entry ending the list: OK. 0962 if (scursor == send) { 0963 return true; 0964 } 0965 // empty entry: ignore. 0966 if (*scursor == ',') { 0967 scursor++; 0968 continue; 0969 } 0970 0971 AddrSpec maybeMsgId; 0972 if (!parseAngleAddr(scursor, send, maybeMsgId, isCRLF)) { 0973 return false; 0974 } 0975 d->msgIdList.append(maybeMsgId); 0976 0977 eatCFWS(scursor, send, isCRLF); 0978 // header end ending the list: OK. 0979 if (scursor == send) { 0980 return true; 0981 } 0982 // regular item separator: eat it. 0983 if (*scursor == ',') { 0984 scursor++; 0985 } 0986 } 0987 return true; 0988 } 0989 0990 QList<QByteArray> Ident::identifiers() const { 0991 QList<QByteArray> rv; 0992 const auto msgIdList = d_func()->msgIdList; 0993 for (const Types::AddrSpec &addr : msgIdList) { 0994 if (!addr.isEmpty()) { 0995 const QString asString = addr.asString(); 0996 if (!asString.isEmpty()) { 0997 rv.append(asString.toLatin1()); // FIXME: change parsing to use QByteArrays 0998 } 0999 } 1000 } 1001 return rv; 1002 } 1003 1004 void Ident::fromIdent(const Ident* ident) 1005 { 1006 d_func()->encCS = ident->d_func()->encCS; 1007 d_func()->msgIdList = ident->d_func()->msgIdList; 1008 d_func()->cachedIdentifier = ident->d_func()->cachedIdentifier; 1009 } 1010 1011 void Ident::appendIdentifier(const QByteArray &id) 1012 { 1013 Q_D(Ident); 1014 QByteArray tmp = id; 1015 if (!tmp.startsWith('<')) { 1016 tmp.prepend('<'); 1017 } 1018 if (!tmp.endsWith('>')) { 1019 tmp.append('>'); 1020 } 1021 AddrSpec msgId; 1022 const char *cursor = tmp.constData(); 1023 if (parseAngleAddr(cursor, cursor + tmp.length(), msgId)) { 1024 d->msgIdList.append(msgId); 1025 } else { 1026 qCWarning(KMIME_LOG) << "Unable to parse address spec!"; 1027 } 1028 } 1029 1030 //-----</Ident>------------------------- 1031 1032 //-----<SingleIdent>------------------------- 1033 1034 //@cond PRIVATE 1035 kmime_mk_trivial_ctor_with_dptr(SingleIdent, Ident) 1036 kmime_mk_dptr_ctor(SingleIdent, Ident) 1037 //@endcond 1038 1039 QByteArray SingleIdent::identifier() const 1040 { 1041 if (d_func()->msgIdList.isEmpty()) { 1042 return {}; 1043 } 1044 1045 if (d_func()->cachedIdentifier.isEmpty()) { 1046 const Types::AddrSpec &addr = d_func()->msgIdList.first(); 1047 if (!addr.isEmpty()) { 1048 const QString asString = addr.asString(); 1049 if (!asString.isEmpty()) { 1050 d_func()->cachedIdentifier = asString.toLatin1();// FIXME: change parsing to use QByteArrays 1051 } 1052 } 1053 } 1054 1055 return d_func()->cachedIdentifier; 1056 } 1057 1058 void SingleIdent::setIdentifier(const QByteArray &id) 1059 { 1060 Q_D(SingleIdent); 1061 d->msgIdList.clear(); 1062 d->cachedIdentifier.clear(); 1063 appendIdentifier(id); 1064 } 1065 1066 bool SingleIdent::parse(const char *&scursor, const char *const send, 1067 bool isCRLF) 1068 { 1069 Q_D(SingleIdent); 1070 if (!Ident::parse(scursor, send, isCRLF)) { 1071 return false; 1072 } 1073 1074 if (d->msgIdList.count() > 1) { 1075 KMIME_WARN << "more than one msg-id in header " 1076 << "allowing only a single one!" 1077 << Qt::endl; 1078 } 1079 return true; 1080 } 1081 1082 //-----</SingleIdent>------------------------- 1083 1084 } // namespace Generics 1085 1086 //-----<ReturnPath>------------------------- 1087 1088 //@cond PRIVATE 1089 kmime_mk_trivial_ctor_with_name_and_dptr(ReturnPath, Generics::Address, Return-Path) 1090 //@endcond 1091 1092 QByteArray ReturnPath::as7BitString(bool withHeaderType) const 1093 { 1094 if (isEmpty()) { 1095 return {}; 1096 } 1097 1098 QByteArray rv; 1099 if (withHeaderType) { 1100 rv += typeIntro(); 1101 } 1102 rv += '<' + d_func()->mailbox.as7BitString(d_func()->encCS) + '>'; 1103 return rv; 1104 } 1105 1106 void ReturnPath::clear() 1107 { 1108 Q_D(ReturnPath); 1109 d->mailbox.setAddress(Types::AddrSpec()); 1110 d->mailbox.setName(QString()); 1111 } 1112 1113 bool ReturnPath::isEmpty() const 1114 { 1115 const Q_D(ReturnPath); 1116 return !d->mailbox.hasAddress() && !d->mailbox.hasName(); 1117 } 1118 1119 bool 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 1173 Generic::Generic() : Generics::Unstructured(new GenericPrivate) 1174 { 1175 } 1176 1177 Generic::Generic(const char *t, int len) : Generics::Unstructured(new GenericPrivate) 1178 { 1179 setType(t, len); 1180 } 1181 1182 Generic::~Generic() 1183 { 1184 Q_D(Generic); 1185 delete d; 1186 d_ptr = nullptr; 1187 } 1188 1189 void Generic::clear() 1190 { 1191 Q_D(Generic); 1192 delete[] d->type; 1193 d->type = nullptr; 1194 Unstructured::clear(); 1195 } 1196 1197 bool Generic::isEmpty() const 1198 { 1199 return d_func()->type == nullptr || Unstructured::isEmpty(); 1200 } 1201 1202 const char *Generic::type() const 1203 { 1204 return d_func()->type; 1205 } 1206 1207 void Generic::setType(const char *type, int len) 1208 { 1209 Q_D(Generic); 1210 if (d->type) { 1211 delete[] d->type; 1212 } 1213 if (type) { 1214 const int l = (len < 0 ? strlen(type) : len) + 1; 1215 d->type = new char[l]; 1216 qstrncpy(d->type, type, l); 1217 } else { 1218 d->type = nullptr; 1219 } 1220 } 1221 1222 //-----<Generic>------------------------------- 1223 1224 //-----<MessageID>----------------------------- 1225 1226 //@cond PRIVATE 1227 kmime_mk_trivial_ctor_with_name(MessageID, Generics::SingleIdent, Message-ID) 1228 //@endcond 1229 1230 void MessageID::generate(const QByteArray &fqdn) 1231 { 1232 setIdentifier('<' + uniqueString() + '@' + fqdn + '>'); 1233 } 1234 1235 //-----</MessageID>---------------------------- 1236 1237 //-----<Control>------------------------------- 1238 1239 //@cond PRIVATE 1240 kmime_mk_trivial_ctor_with_name_and_dptr(Control, Generics::Structured, Control) 1241 //@endcond 1242 1243 QByteArray Control::as7BitString(bool withHeaderType) const 1244 { 1245 const Q_D(Control); 1246 if (isEmpty()) { 1247 return {}; 1248 } 1249 1250 QByteArray rv; 1251 if (withHeaderType) { 1252 rv += typeIntro(); 1253 } 1254 1255 rv += d->name; 1256 if (!d->parameter.isEmpty()) { 1257 rv += ' ' + d->parameter; 1258 } 1259 return rv; 1260 } 1261 1262 void Control::clear() 1263 { 1264 Q_D(Control); 1265 d->name.clear(); 1266 d->parameter.clear(); 1267 } 1268 1269 bool Control::isEmpty() const 1270 { 1271 return d_func()->name.isEmpty(); 1272 } 1273 1274 QByteArray Control::controlType() const 1275 { 1276 return d_func()->name; 1277 } 1278 1279 QByteArray Control::parameter() const 1280 { 1281 return d_func()->parameter; 1282 } 1283 1284 bool Control::isCancel() const 1285 { 1286 return d_func()->name.toLower() == "cancel"; 1287 } 1288 1289 void Control::setCancel(const QByteArray &msgid) 1290 { 1291 Q_D(Control); 1292 d->name = "cancel"; 1293 d->parameter = msgid; 1294 } 1295 1296 bool Control::parse(const char *&scursor, const char *const send, bool isCRLF) 1297 { 1298 Q_D(Control); 1299 clear(); 1300 eatCFWS(scursor, send, isCRLF); 1301 if (scursor == send) { 1302 return false; 1303 } 1304 const char *start = scursor; 1305 while (scursor != send && !isspace(*scursor)) { 1306 ++scursor; 1307 } 1308 d->name = QByteArray(start, scursor - start); 1309 eatCFWS(scursor, send, isCRLF); 1310 d->parameter = QByteArray(scursor, send - scursor); 1311 return true; 1312 } 1313 1314 //-----</Control>------------------------------ 1315 1316 //-----<MailCopiesTo>-------------------------- 1317 1318 //@cond PRIVATE 1319 kmime_mk_trivial_ctor_with_name_and_dptr(MailCopiesTo, 1320 Generics::AddressList, Mail-Copies-To) 1321 //@endcond 1322 1323 QByteArray MailCopiesTo::as7BitString(bool withHeaderType) const 1324 { 1325 QByteArray rv; 1326 if (withHeaderType) { 1327 rv += typeIntro(); 1328 } 1329 if (!AddressList::isEmpty()) { 1330 rv += AddressList::as7BitString(false); 1331 } else { 1332 if (d_func()->alwaysCopy) { 1333 rv += "poster"; 1334 } else if (d_func()->neverCopy) { 1335 rv += "nobody"; 1336 } 1337 } 1338 return rv; 1339 } 1340 1341 QString MailCopiesTo::asUnicodeString() const 1342 { 1343 if (!AddressList::isEmpty()) { 1344 return AddressList::asUnicodeString(); 1345 } 1346 if (d_func()->alwaysCopy) { 1347 return QStringLiteral("poster"); 1348 } 1349 if (d_func()->neverCopy) { 1350 return QStringLiteral("nobody"); 1351 } 1352 return {}; 1353 } 1354 1355 void MailCopiesTo::clear() 1356 { 1357 Q_D(MailCopiesTo); 1358 AddressList::clear(); 1359 d->alwaysCopy = false; 1360 d->neverCopy = false; 1361 } 1362 1363 bool MailCopiesTo::isEmpty() const 1364 { 1365 return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy); 1366 } 1367 1368 bool MailCopiesTo::alwaysCopy() const 1369 { 1370 return !AddressList::isEmpty() || d_func()->alwaysCopy; 1371 } 1372 1373 void MailCopiesTo::setAlwaysCopy() 1374 { 1375 Q_D(MailCopiesTo); 1376 clear(); 1377 d->alwaysCopy = true; 1378 } 1379 1380 bool MailCopiesTo::neverCopy() const 1381 { 1382 return d_func()->neverCopy; 1383 } 1384 1385 void MailCopiesTo::setNeverCopy() 1386 { 1387 Q_D(MailCopiesTo); 1388 clear(); 1389 d->neverCopy = true; 1390 } 1391 1392 bool MailCopiesTo::parse(const char *&scursor, const char *const send, 1393 bool isCRLF) 1394 { 1395 Q_D(MailCopiesTo); 1396 clear(); 1397 if (send - scursor == 5) { 1398 if (qstrnicmp("never", scursor, 5) == 0) { 1399 d->neverCopy = true; 1400 return true; 1401 } 1402 } 1403 if (send - scursor == 6) { 1404 if (qstrnicmp("always", scursor, 6) == 0 || qstrnicmp("poster", scursor, 6) == 0) { 1405 d->alwaysCopy = true; 1406 return true; 1407 } 1408 if (qstrnicmp("nobody", scursor, 6) == 0) { 1409 d->neverCopy = true; 1410 return true; 1411 } 1412 } 1413 return AddressList::parse(scursor, send, isCRLF); 1414 } 1415 1416 //-----</MailCopiesTo>------------------------- 1417 1418 //-----<Date>---------------------------------- 1419 1420 //@cond PRIVATE 1421 kmime_mk_trivial_ctor_with_name_and_dptr(Date, Generics::Structured, Date) 1422 //@endcond 1423 1424 QByteArray Date::as7BitString(bool withHeaderType) const 1425 { 1426 if (isEmpty()) { 1427 return {}; 1428 } 1429 1430 QByteArray rv; 1431 if (withHeaderType) { 1432 rv += typeIntro(); 1433 } 1434 //QT5 fix port to QDateTime Qt::RFC2822Date is not enough we need to fix it. We need to use QLocale("C") + add "ddd, "; 1435 //rv += d_func()->dateTime.toString( Qt::RFC2822Date ).toLatin1(); 1436 rv += QLocale::c().toString(d_func()->dateTime, QStringLiteral("ddd, ")).toLatin1(); 1437 rv += d_func()->dateTime.toString(Qt::RFC2822Date).toLatin1(); 1438 1439 return rv; 1440 } 1441 1442 void Date::clear() { 1443 Q_D(Date); 1444 d->dateTime = QDateTime(); 1445 } 1446 1447 bool Date::isEmpty() const { 1448 return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid(); 1449 } 1450 1451 QDateTime Date::dateTime() const { 1452 return d_func()->dateTime; 1453 } 1454 1455 void Date::setDateTime(const QDateTime & dt) { 1456 Q_D(Date); 1457 d->dateTime = dt; 1458 } 1459 1460 int Date::ageInDays() const { 1461 const QDate today = QDate::currentDate(); 1462 return dateTime().date().daysTo(today); 1463 } 1464 1465 bool Date::parse(const char *&scursor, const char *const send, bool isCRLF) { 1466 Q_D(Date); 1467 const char *start = scursor; 1468 bool result = parseDateTime(scursor, send, d->dateTime, isCRLF); 1469 if (!result) { 1470 result = parseQDateTime(start, send, d->dateTime, isCRLF); 1471 } 1472 return result; 1473 } 1474 1475 //-----</Date>--------------------------------- 1476 1477 //-----<Newsgroups>---------------------------- 1478 1479 //@cond PRIVATE 1480 kmime_mk_trivial_ctor_with_name_and_dptr(Newsgroups, Generics::Structured, Newsgroups) 1481 kmime_mk_trivial_ctor_with_name(FollowUpTo, Newsgroups, Followup-To) 1482 //@endcond 1483 1484 QByteArray Newsgroups::as7BitString(bool withHeaderType) const { 1485 const Q_D(Newsgroups); 1486 if (isEmpty()) { 1487 return {}; 1488 } 1489 1490 QByteArray rv; 1491 if (withHeaderType) { 1492 rv += typeIntro(); 1493 } 1494 1495 for (int i = 0; i < d->groups.count(); ++i) { 1496 rv += d->groups[ i ]; 1497 if (i != d->groups.count() - 1) { 1498 rv += ','; 1499 } 1500 } 1501 return rv; 1502 } 1503 1504 void Newsgroups::fromUnicodeString(const QString & s, const QByteArray & b) { 1505 Q_UNUSED(b) 1506 Q_D(Newsgroups); 1507 from7BitString(s.toUtf8()); 1508 d->encCS = cachedCharset("UTF-8"); 1509 } 1510 1511 QString Newsgroups::asUnicodeString() const { 1512 return QString::fromUtf8(as7BitString(false)); 1513 } 1514 1515 void Newsgroups::clear() { 1516 Q_D(Newsgroups); 1517 d->groups.clear(); 1518 } 1519 1520 bool Newsgroups::isEmpty() const { 1521 return d_func()->groups.isEmpty(); 1522 } 1523 1524 QList<QByteArray> Newsgroups::groups() const { return d_func()->groups; } 1525 1526 void Newsgroups::setGroups(const QList<QByteArray> &groups) { 1527 Q_D(Newsgroups); 1528 d->groups = groups; 1529 } 1530 1531 bool Newsgroups::isCrossposted() const { 1532 return d_func()->groups.count() >= 2; 1533 } 1534 1535 bool Newsgroups::parse(const char *&scursor, const char *const send, bool isCRLF) { 1536 Q_D(Newsgroups); 1537 clear(); 1538 while (true) { 1539 eatCFWS(scursor, send, isCRLF); 1540 if (scursor != send && *scursor == ',') { 1541 ++scursor; 1542 } 1543 eatCFWS(scursor, send, isCRLF); 1544 if (scursor == send) { 1545 return true; 1546 } 1547 const char *start = scursor; 1548 while (scursor != send && !isspace(*scursor) && *scursor != ',') { 1549 ++scursor; 1550 } 1551 QByteArray group(start, scursor - start); 1552 d->groups.append(group); 1553 } 1554 return true; 1555 } 1556 1557 //-----</Newsgroups>--------------------------- 1558 1559 //-----<Lines>--------------------------------- 1560 1561 //@cond PRIVATE 1562 kmime_mk_trivial_ctor_with_name_and_dptr(Lines, Generics::Structured, Lines) 1563 //@endcond 1564 1565 QByteArray Lines::as7BitString(bool withHeaderType) const { 1566 if (isEmpty()) { 1567 return {}; 1568 } 1569 1570 QByteArray num; 1571 num.setNum(d_func()->lines); 1572 1573 if (withHeaderType) { 1574 return typeIntro() + num; 1575 } 1576 return num; 1577 } 1578 1579 QString Lines::asUnicodeString() const { 1580 if (isEmpty()) { 1581 return {}; 1582 } 1583 return QString::number(d_func()->lines); 1584 } 1585 1586 void Lines::clear() { 1587 Q_D(Lines); 1588 d->lines = -1; 1589 } 1590 1591 bool Lines::isEmpty() const { 1592 return d_func()->lines == -1; 1593 } 1594 1595 int Lines::numberOfLines() const { 1596 return d_func()->lines; 1597 } 1598 1599 void Lines::setNumberOfLines(int lines) { 1600 Q_D(Lines); 1601 d->lines = lines; 1602 } 1603 1604 bool Lines::parse(const char *&scursor, const char *const send, bool isCRLF) { 1605 Q_D(Lines); 1606 eatCFWS(scursor, send, isCRLF); 1607 if (parseDigits(scursor, send, d->lines) == 0) { 1608 clear(); 1609 return false; 1610 } 1611 return true; 1612 } 1613 1614 //-----</Lines>-------------------------------- 1615 1616 //-----<Content-Type>-------------------------- 1617 1618 //@cond PRIVATE 1619 kmime_mk_trivial_ctor_with_name_and_dptr(ContentType, Generics::Parametrized, 1620 Content-Type) 1621 //@endcond 1622 1623 bool ContentType::isEmpty() const { 1624 return d_func()->mimeType.isEmpty(); 1625 } 1626 1627 void ContentType::clear() { 1628 Q_D(ContentType); 1629 d->mimeType.clear(); 1630 Parametrized::clear(); 1631 } 1632 1633 QByteArray ContentType::as7BitString(bool withHeaderType) const { 1634 if (isEmpty()) { 1635 return {}; 1636 } 1637 1638 QByteArray rv; 1639 if (withHeaderType) { 1640 rv += typeIntro(); 1641 } 1642 1643 rv += mimeType(); 1644 if (!Parametrized::isEmpty()) { 1645 rv += "; " + Parametrized::as7BitString(false); 1646 } 1647 1648 return rv; 1649 } 1650 1651 QByteArray ContentType::mimeType() const { 1652 Q_D(const ContentType); 1653 return d->mimeType; 1654 } 1655 1656 QByteArray ContentType::mediaType() const { 1657 Q_D(const ContentType); 1658 const int pos = d->mimeType.indexOf('/'); 1659 if (pos < 0) { 1660 return d->mimeType; 1661 } else { 1662 return d->mimeType.left(pos); 1663 } 1664 } 1665 1666 QByteArray ContentType::subType() const { 1667 Q_D(const ContentType); 1668 const int pos = d->mimeType.indexOf('/'); 1669 if (pos < 0) { 1670 return {}; 1671 } else { 1672 return d->mimeType.mid(pos + 1); 1673 } 1674 } 1675 1676 void ContentType::setMimeType(const QByteArray & mimeType) { 1677 Q_D(ContentType); 1678 d->mimeType = mimeType; 1679 } 1680 1681 bool ContentType::isMediatype(const char *mediatype) const { 1682 Q_D(const ContentType); 1683 const int len = strlen(mediatype); 1684 return qstrnicmp(d->mimeType.constData(), mediatype, len) == 0 && 1685 (d->mimeType.at(len) == '/' || d->mimeType.size() == len); 1686 } 1687 1688 bool ContentType::isSubtype(const char *subtype) const { 1689 Q_D(const ContentType); 1690 const int pos = d->mimeType.indexOf('/'); 1691 if (pos < 0) { 1692 return false; 1693 } 1694 const int len = strlen(subtype); 1695 return qstrnicmp(d->mimeType.constData() + pos + 1, subtype, len) == 0 && 1696 d->mimeType.size() == pos + len + 1; 1697 } 1698 1699 bool ContentType::isMimeType(const char* mimeType) const 1700 { 1701 Q_D(const ContentType); 1702 return qstricmp(d->mimeType.constData(), mimeType) == 0; 1703 } 1704 1705 bool ContentType::isText() const { 1706 return (isMediatype("text") || isEmpty()); 1707 } 1708 1709 bool ContentType::isPlainText() const { 1710 return (qstricmp(d_func()->mimeType.constData(), "text/plain") == 0 || isEmpty()); 1711 } 1712 1713 bool ContentType::isHTMLText() const { 1714 return qstricmp(d_func()->mimeType.constData(), "text/html") == 0; 1715 } 1716 1717 bool ContentType::isImage() const { 1718 return isMediatype("image"); 1719 } 1720 1721 bool ContentType::isMultipart() const { 1722 return isMediatype("multipart"); 1723 } 1724 1725 bool ContentType::isPartial() const { 1726 return qstricmp(d_func()->mimeType.constData(), "message/partial") == 0; 1727 } 1728 1729 QByteArray ContentType::charset() const { 1730 QByteArray ret = parameter(QStringLiteral("charset")).toLatin1(); 1731 if (ret.isEmpty()) { 1732 //return the default-charset if necessary 1733 ret = Content::defaultCharset(); 1734 } 1735 return ret; 1736 } 1737 1738 void ContentType::setCharset(const QByteArray & s) { 1739 setParameter(QStringLiteral("charset"), QString::fromLatin1(s)); 1740 } 1741 1742 QByteArray ContentType::boundary() const { 1743 return parameter(QStringLiteral("boundary")).toLatin1(); 1744 } 1745 1746 void ContentType::setBoundary(const QByteArray & s) { 1747 setParameter(QStringLiteral("boundary"), QString::fromLatin1(s)); 1748 } 1749 1750 QString ContentType::name() const { 1751 return parameter(QStringLiteral("name")); 1752 } 1753 1754 void ContentType::setName(const QString & s, const QByteArray & cs) { 1755 Q_D(ContentType); 1756 d->encCS = cs; 1757 setParameter(QStringLiteral("name"), s); 1758 } 1759 1760 QByteArray ContentType::id() const { 1761 return parameter(QStringLiteral("id")).toLatin1(); 1762 } 1763 1764 void ContentType::setId(const QByteArray & s) { 1765 setParameter(QStringLiteral("id"), QString::fromLatin1(s)); 1766 } 1767 1768 int ContentType::partialNumber() const { 1769 QByteArray p = parameter(QStringLiteral("number")).toLatin1(); 1770 if (!p.isEmpty()) { 1771 return p.toInt(); 1772 } else { 1773 return -1; 1774 } 1775 } 1776 1777 int ContentType::partialCount() const { 1778 QByteArray p = parameter(QStringLiteral("total")).toLatin1(); 1779 if (!p.isEmpty()) { 1780 return p.toInt(); 1781 } else { 1782 return -1; 1783 } 1784 } 1785 1786 void ContentType::setPartialParams(int total, int number) { 1787 setParameter(QStringLiteral("number"), QString::number(number)); 1788 setParameter(QStringLiteral("total"), QString::number(total)); 1789 } 1790 1791 bool ContentType::parse(const char *&scursor, const char *const send, 1792 bool isCRLF) { 1793 Q_D(ContentType); 1794 // content-type: type "/" subtype *(";" parameter) 1795 clear(); 1796 eatCFWS(scursor, send, isCRLF); 1797 if (scursor == send) { 1798 return false; // empty header 1799 } 1800 1801 // type 1802 QPair<const char *, int> maybeMimeType; 1803 if (!parseToken(scursor, send, maybeMimeType, ParseTokenNoFlag)) { 1804 return false; 1805 } 1806 // subtype 1807 eatCFWS(scursor, send, isCRLF); 1808 if (scursor == send || *scursor != '/') { 1809 return false; 1810 } 1811 scursor++; 1812 eatCFWS(scursor, send, isCRLF); 1813 if (scursor == send) { 1814 return false; 1815 } 1816 QPair<const char *, int> maybeSubType; 1817 if (!parseToken(scursor, send, maybeSubType, ParseTokenNoFlag)) { 1818 return false; 1819 } 1820 1821 d->mimeType.reserve(maybeMimeType.second + maybeSubType.second + 1); 1822 d->mimeType = QByteArray(maybeMimeType.first, maybeMimeType.second).toLower() 1823 + '/' + QByteArray(maybeSubType.first, maybeSubType.second).toLower(); 1824 1825 // parameter list 1826 eatCFWS(scursor, send, isCRLF); 1827 if (scursor == send) { 1828 return true; // no parameters 1829 } 1830 if (*scursor != ';') { 1831 return false; 1832 } 1833 scursor++; 1834 1835 if (!Parametrized::parse(scursor, send, isCRLF)) { 1836 return false; 1837 } 1838 1839 return true; 1840 } 1841 1842 //-----</Content-Type>------------------------- 1843 1844 //-----<ContentID>---------------------- 1845 1846 kmime_mk_trivial_ctor_with_name_and_dptr(ContentID, SingleIdent, Content-ID) 1847 kmime_mk_dptr_ctor(ContentID, SingleIdent) 1848 1849 bool ContentID::parse(const char *&scursor, const char *const send, bool isCRLF) { 1850 Q_D(ContentID); 1851 // Content-id := "<" contentid ">" 1852 // contentid := now whitespaces 1853 1854 const char *origscursor = scursor; 1855 if (!SingleIdent::parse(scursor, send, isCRLF)) { 1856 scursor = origscursor; 1857 d->msgIdList.clear(); 1858 d->cachedIdentifier.clear(); 1859 1860 while (scursor != send) { 1861 eatCFWS(scursor, send, isCRLF); 1862 // empty entry ending the list: OK. 1863 if (scursor == send) { 1864 return true; 1865 } 1866 // empty entry: ignore. 1867 if (*scursor == ',') { 1868 scursor++; 1869 continue; 1870 } 1871 1872 AddrSpec maybeContentId; 1873 // Almost parseAngleAddr 1874 if (scursor == send || *scursor != '<') { 1875 return false; 1876 } 1877 scursor++; // eat '<' 1878 1879 eatCFWS(scursor, send, isCRLF); 1880 if (scursor == send) { 1881 return false; 1882 } 1883 1884 // Save chars until '>'' 1885 QByteArray result; 1886 if (!parseDotAtom(scursor, send, result, false)) { 1887 return false; 1888 } 1889 1890 eatCFWS(scursor, send, isCRLF); 1891 if (scursor == send || *scursor != '>') { 1892 return false; 1893 } 1894 scursor++; 1895 // /Almost parseAngleAddr 1896 1897 maybeContentId.localPart = QString::fromLatin1(result); // FIXME: just use QByteArray instead of AddrSpec in msgIdList? 1898 d->msgIdList.append(maybeContentId); 1899 1900 eatCFWS(scursor, send, isCRLF); 1901 // header end ending the list: OK. 1902 if (scursor == send) { 1903 return true; 1904 } 1905 // regular item separator: eat it. 1906 if (*scursor == ',') { 1907 scursor++; 1908 } 1909 } 1910 return true; 1911 } else { 1912 return true; 1913 } 1914 } 1915 1916 //-----</ContentID>---------------------- 1917 1918 //-----<ContentTransferEncoding>---------------------------- 1919 1920 //@cond PRIVATE 1921 kmime_mk_trivial_ctor_with_name_and_dptr(ContentTransferEncoding, 1922 Generics::Token, Content-Transfer-Encoding) 1923 //@endcond 1924 1925 typedef struct { 1926 const char *s; 1927 int e; 1928 } encTableType; 1929 1930 static const encTableType encTable[] = { 1931 { "7Bit", CE7Bit }, 1932 { "8Bit", CE8Bit }, 1933 { "quoted-printable", CEquPr }, 1934 { "base64", CEbase64 }, 1935 { "x-uuencode", CEuuenc }, 1936 { "binary", CEbinary }, 1937 { nullptr, 0} 1938 }; 1939 1940 void ContentTransferEncoding::clear() { 1941 Q_D(ContentTransferEncoding); 1942 d->decoded = true; 1943 d->cte = CE7Bit; 1944 Token::clear(); 1945 } 1946 1947 contentEncoding ContentTransferEncoding::encoding() const { 1948 return d_func()->cte; 1949 } 1950 1951 void ContentTransferEncoding::setEncoding(contentEncoding e) { 1952 Q_D(ContentTransferEncoding); 1953 d->cte = e; 1954 1955 for (int i = 0; encTable[i].s != nullptr; ++i) { 1956 if (d->cte == encTable[i].e) { 1957 setToken(encTable[i].s); 1958 break; 1959 } 1960 } 1961 } 1962 1963 bool ContentTransferEncoding::isDecoded() const { 1964 return d_func()->decoded; 1965 } 1966 1967 void ContentTransferEncoding::setDecoded(bool decoded) { 1968 Q_D(ContentTransferEncoding); 1969 d->decoded = decoded; 1970 } 1971 1972 bool ContentTransferEncoding::needToEncode() const { 1973 const Q_D(ContentTransferEncoding); 1974 return d->decoded && (d->cte == CEquPr || d->cte == CEbase64); 1975 } 1976 1977 bool ContentTransferEncoding::parse(const char *&scursor, 1978 const char *const send, bool isCRLF) { 1979 Q_D(ContentTransferEncoding); 1980 clear(); 1981 if (!Token::parse(scursor, send, isCRLF)) { 1982 return false; 1983 } 1984 1985 // TODO: error handling in case of an unknown encoding? 1986 for (int i = 0; encTable[i].s != nullptr; ++i) { 1987 if (qstricmp(token().constData(), encTable[i].s) == 0) { 1988 d->cte = (contentEncoding)encTable[i].e; 1989 break; 1990 } 1991 } 1992 d->decoded = (d->cte == CE7Bit || d->cte == CE8Bit); 1993 return true; 1994 } 1995 1996 //-----</ContentTransferEncoding>--------------------------- 1997 1998 //-----<ContentDisposition>-------------------------- 1999 2000 //@cond PRIVATE 2001 kmime_mk_trivial_ctor_with_name_and_dptr(ContentDisposition, 2002 Generics::Parametrized, Content-Disposition) 2003 //@endcond 2004 2005 QByteArray ContentDisposition::as7BitString(bool withHeaderType) const { 2006 if (isEmpty()) { 2007 return {}; 2008 } 2009 2010 QByteArray rv; 2011 if (withHeaderType) { 2012 rv += typeIntro(); 2013 } 2014 2015 if (d_func()->disposition == CDattachment) { 2016 rv += "attachment"; 2017 } else if (d_func()->disposition == CDinline) { 2018 rv += "inline"; 2019 } else { 2020 return {}; 2021 } 2022 2023 if (!Parametrized::isEmpty()) { 2024 rv += "; " + Parametrized::as7BitString(false); 2025 } 2026 2027 return rv; 2028 } 2029 2030 bool ContentDisposition::isEmpty() const { 2031 return d_func()->disposition == CDInvalid; 2032 } 2033 2034 void ContentDisposition::clear() { 2035 Q_D(ContentDisposition); 2036 d->disposition = CDInvalid; 2037 Parametrized::clear(); 2038 } 2039 2040 contentDisposition ContentDisposition::disposition() const { 2041 return d_func()->disposition; 2042 } 2043 2044 void ContentDisposition::setDisposition(contentDisposition disp) { 2045 Q_D(ContentDisposition); 2046 d->disposition = disp; 2047 } 2048 2049 QString KMime::Headers::ContentDisposition::filename() const { 2050 return parameter(QStringLiteral("filename")); 2051 } 2052 2053 void ContentDisposition::setFilename(const QString & filename) { 2054 setParameter(QStringLiteral("filename"), filename); 2055 } 2056 2057 bool ContentDisposition::parse(const char *&scursor, const char *const send, 2058 bool isCRLF) { 2059 Q_D(ContentDisposition); 2060 clear(); 2061 2062 // token 2063 QByteArray token; 2064 eatCFWS(scursor, send, isCRLF); 2065 if (scursor == send) { 2066 return false; 2067 } 2068 2069 QPair<const char *, int> maybeToken; 2070 if (!parseToken(scursor, send, maybeToken, ParseTokenNoFlag)) { 2071 return false; 2072 } 2073 2074 token = QByteArray(maybeToken.first, maybeToken.second).toLower(); 2075 if (token == "inline") { 2076 d->disposition = CDinline; 2077 } else if (token == "attachment") { 2078 d->disposition = CDattachment; 2079 } else { 2080 return false; 2081 } 2082 2083 // parameter list 2084 eatCFWS(scursor, send, isCRLF); 2085 if (scursor == send) { 2086 return true; // no parameters 2087 } 2088 2089 if (*scursor != ';') { 2090 return false; 2091 } 2092 scursor++; 2093 2094 return Parametrized::parse(scursor, send, isCRLF); 2095 } 2096 2097 //-----</ContentDisposition>------------------------- 2098 2099 //@cond PRIVATE 2100 kmime_mk_trivial_ctor_with_name(Subject, Generics::Unstructured, Subject) 2101 //@endcond 2102 2103 Base *createHeader(const QByteArray & type) { 2104 return HeaderFactory::createHeader(type); 2105 } 2106 2107 //@cond PRIVATE 2108 kmime_mk_trivial_ctor_with_name(ContentDescription, 2109 Generics::Unstructured, Content-Description) 2110 kmime_mk_trivial_ctor_with_name(ContentLocation, 2111 Generics::Unstructured, Content-Location) 2112 kmime_mk_trivial_ctor_with_name(From, Generics::MailboxList, From) 2113 kmime_mk_trivial_ctor_with_name(Sender, Generics::SingleMailbox, Sender) 2114 kmime_mk_trivial_ctor_with_name(To, Generics::AddressList, To) 2115 kmime_mk_trivial_ctor_with_name(Cc, Generics::AddressList, Cc) 2116 kmime_mk_trivial_ctor_with_name(Bcc, Generics::AddressList, Bcc) 2117 kmime_mk_trivial_ctor_with_name(ReplyTo, Generics::AddressList, Reply-To) 2118 kmime_mk_trivial_ctor_with_name(Keywords, Generics::PhraseList, Keywords) 2119 kmime_mk_trivial_ctor_with_name(MIMEVersion, Generics::DotAtom, MIME-Version) 2120 kmime_mk_trivial_ctor_with_name(Supersedes, Generics::SingleIdent, Supersedes) 2121 kmime_mk_trivial_ctor_with_name(InReplyTo, Generics::Ident, In-Reply-To) 2122 kmime_mk_trivial_ctor_with_name(References, Generics::Ident, References) 2123 kmime_mk_trivial_ctor_with_name(Organization, Generics::Unstructured, Organization) 2124 kmime_mk_trivial_ctor_with_name(UserAgent, Generics::Unstructured, User-Agent) 2125 //@endcond 2126 2127 } // namespace Headers 2128 2129 } // namespace KMime