File indexing completed on 2023-10-03 04:47:28
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.h" 0033 #include "kmime_content.h" 0034 #include "kmime_headerfactory_p.h" 0035 #include "kmime_debug.h" 0036 #include "kmime_warning.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 QVector<QByteArray> MailboxList::addresses() const 0359 { 0360 QVector<QByteArray> rv; 0361 rv.reserve(d_func()->mailboxList.count()); 0362 const auto mailboxList = d_func()->mailboxList; 0363 for (const Types::Mailbox &mbox : mailboxList) { 0364 rv.append(mbox.address()); 0365 } 0366 return rv; 0367 } 0368 0369 QStringList MailboxList::displayNames() const 0370 { 0371 Q_D(const MailboxList); 0372 QStringList rv; 0373 rv.reserve(d->mailboxList.count()); 0374 for (const Types::Mailbox &mbox : std::as_const(d->mailboxList)) { 0375 if (mbox.hasName()) { 0376 rv.append(mbox.name()); 0377 } else { 0378 rv.append(QString::fromLatin1(mbox.address())); 0379 } 0380 } 0381 return rv; 0382 } 0383 0384 QString MailboxList::displayString() const 0385 { 0386 Q_D(const MailboxList); 0387 if (d->mailboxList.size() == 1) { // fast-path to avoid temporary QStringList in the common case of just one From address 0388 const auto& mbox = d->mailboxList.at(0); 0389 if (mbox.hasName()) { 0390 return mbox.name(); 0391 } else { 0392 return QString::fromLatin1(mbox.address()); 0393 } 0394 } 0395 return displayNames().join(QLatin1String(", ")); 0396 } 0397 0398 Types::Mailbox::List MailboxList::mailboxes() const 0399 { 0400 return d_func()->mailboxList; 0401 } 0402 0403 bool MailboxList::parse(const char *&scursor, const char *const send, 0404 bool isCRLF) 0405 { 0406 Q_D(MailboxList); 0407 // examples: 0408 // from := "From:" mailbox-list CRLF 0409 // sender := "Sender:" mailbox CRLF 0410 0411 // parse an address-list: 0412 QVector<Types::Address> maybeAddressList; 0413 if (!parseAddressList(scursor, send, maybeAddressList, isCRLF)) { 0414 return false; 0415 } 0416 0417 d->mailboxList.clear(); 0418 d->mailboxList.reserve(maybeAddressList.count()); 0419 0420 // extract the mailboxes and complain if there are groups: 0421 for (const auto &it : std::as_const(maybeAddressList)) { 0422 if (!(it).displayName.isEmpty()) { 0423 KMIME_WARN << "mailbox groups in header disallowing them! Name: \"" 0424 << (it).displayName << "\"" 0425 << Qt::endl 0426 ; 0427 } 0428 d->mailboxList += (it).mailboxList; 0429 } 0430 return true; 0431 } 0432 0433 //-----</MailboxList>------------------------- 0434 0435 //-----<SingleMailbox>------------------------- 0436 0437 //@cond PRIVATE 0438 kmime_mk_trivial_ctor_with_dptr(SingleMailbox, MailboxList) 0439 //@endcond 0440 0441 bool SingleMailbox::parse(const char *&scursor, const char *const send, 0442 bool isCRLF) 0443 { 0444 Q_D(MailboxList); 0445 if (!MailboxList::parse(scursor, send, isCRLF)) { 0446 return false; 0447 } 0448 0449 if (d->mailboxList.count() > 1) { 0450 KMIME_WARN << "multiple mailboxes in header allowing only a single one!" 0451 << Qt::endl; 0452 } 0453 return true; 0454 } 0455 0456 //-----</SingleMailbox>------------------------- 0457 0458 //-----<AddressList>------------------------- 0459 0460 //@cond PRIVATE 0461 kmime_mk_trivial_ctor_with_dptr(AddressList, Address) 0462 kmime_mk_dptr_ctor(AddressList, Address) 0463 //@endcond 0464 0465 QByteArray AddressList::as7BitString(bool withHeaderType) const 0466 { 0467 const Q_D(AddressList); 0468 if (d->addressList.isEmpty()) { 0469 return {}; 0470 } 0471 0472 QByteArray rv; 0473 if (withHeaderType) { 0474 rv = typeIntro(); 0475 } 0476 for (const Types::Address &addr : std::as_const(d->addressList)) { 0477 const auto mailBoxList = addr.mailboxList; 0478 for (const Types::Mailbox &mbox : mailBoxList) { 0479 rv += mbox.as7BitString(d->encCS); 0480 rv += ", "; 0481 } 0482 } 0483 rv.resize(rv.length() - 2); 0484 return rv; 0485 } 0486 0487 void AddressList::fromUnicodeString(const QString &s, const QByteArray &b) 0488 { 0489 Q_D(AddressList); 0490 d->encCS = cachedCharset(b); 0491 from7BitString(encodeRFC2047Sentence(s, b)); 0492 } 0493 0494 QString AddressList::asUnicodeString() const 0495 { 0496 Q_D(const AddressList); 0497 QStringList rv; 0498 for (const Types::Address &addr : std::as_const(d->addressList)) { 0499 rv.reserve(rv.size() + addr.mailboxList.size()); 0500 const auto mailboxList = addr.mailboxList; 0501 for (const Types::Mailbox &mbox : mailboxList) { 0502 rv.append(mbox.prettyAddress()); 0503 } 0504 } 0505 return rv.join(QLatin1String(", ")); 0506 } 0507 0508 void AddressList::clear() 0509 { 0510 Q_D(AddressList); 0511 d->addressList.clear(); 0512 } 0513 0514 bool AddressList::isEmpty() const 0515 { 0516 return d_func()->addressList.isEmpty(); 0517 } 0518 0519 void AddressList::addAddress(const Types::Mailbox &mbox) 0520 { 0521 Q_D(AddressList); 0522 Types::Address addr; 0523 addr.mailboxList.append(mbox); 0524 d->addressList.append(addr); 0525 } 0526 0527 void AddressList::addAddress(const QByteArray &address, 0528 const QString &displayName) 0529 { 0530 Q_D(AddressList); 0531 Types::Address addr; 0532 Types::Mailbox mbox; 0533 if (stringToMailbox(address, displayName, mbox)) { 0534 addr.mailboxList.append(mbox); 0535 d->addressList.append(addr); 0536 } 0537 } 0538 0539 QVector<QByteArray> AddressList::addresses() const 0540 { 0541 QVector<QByteArray> rv; 0542 const auto addressList = d_func()->addressList; 0543 for (const Types::Address &addr : addressList) { 0544 const auto mailboxList = addr.mailboxList; 0545 for (const Types::Mailbox &mbox : mailboxList) { 0546 rv.append(mbox.address()); 0547 } 0548 } 0549 return rv; 0550 } 0551 0552 QStringList AddressList::displayNames() const 0553 { 0554 Q_D(const AddressList); 0555 QStringList rv; 0556 for (const Types::Address &addr : std::as_const(d->addressList)) { 0557 const auto mailboxList = addr.mailboxList; 0558 for (const Types::Mailbox &mbox : mailboxList) { 0559 if (mbox.hasName()) { 0560 rv.append(mbox.name()); 0561 } else { 0562 rv.append(QString::fromLatin1(mbox.address())); 0563 } 0564 } 0565 } 0566 return rv; 0567 } 0568 0569 QString AddressList::displayString() const 0570 { 0571 // optimize for single entry and avoid creation of the QStringList in that case? 0572 return displayNames().join(QLatin1String(", ")); 0573 } 0574 0575 Types::Mailbox::List AddressList::mailboxes() const 0576 { 0577 Types::Mailbox::List rv; 0578 const auto addressList = d_func()->addressList; 0579 for (const Types::Address &addr : addressList) { 0580 const auto mailboxList = addr.mailboxList; 0581 for (const Types::Mailbox &mbox : mailboxList) { 0582 rv.append(mbox); 0583 } 0584 } 0585 return rv; 0586 } 0587 0588 bool AddressList::parse(const char *&scursor, const char *const send, 0589 bool isCRLF) 0590 { 0591 Q_D(AddressList); 0592 QVector<Types::Address> maybeAddressList; 0593 if (!parseAddressList(scursor, send, maybeAddressList, isCRLF)) { 0594 return false; 0595 } 0596 0597 d->addressList = maybeAddressList; 0598 return true; 0599 } 0600 0601 //-----</AddressList>------------------------- 0602 0603 //-----<Token>------------------------- 0604 0605 //@cond PRIVATE 0606 kmime_mk_trivial_ctor_with_dptr(Token, Structured) 0607 kmime_mk_dptr_ctor(Token, Structured) 0608 //@endcond 0609 0610 QByteArray Token::as7BitString(bool withHeaderType) const 0611 { 0612 if (isEmpty()) { 0613 return {}; 0614 } 0615 if (withHeaderType) { 0616 return typeIntro() + d_func()->token; 0617 } 0618 return d_func()->token; 0619 } 0620 0621 void Token::clear() 0622 { 0623 Q_D(Token); 0624 d->token.clear(); 0625 } 0626 0627 bool Token::isEmpty() const 0628 { 0629 return d_func()->token.isEmpty(); 0630 } 0631 0632 QByteArray Token::token() const 0633 { 0634 return d_func()->token; 0635 } 0636 0637 void Token::setToken(const QByteArray &t) 0638 { 0639 Q_D(Token); 0640 d->token = t; 0641 } 0642 0643 bool Token::parse(const char *&scursor, const char *const send, bool isCRLF) 0644 { 0645 Q_D(Token); 0646 clear(); 0647 eatCFWS(scursor, send, isCRLF); 0648 // must not be empty: 0649 if (scursor == send) { 0650 return false; 0651 } 0652 0653 QPair<const char *, int> maybeToken; 0654 if (!parseToken(scursor, send, maybeToken, ParseTokenNoFlag)) { 0655 return false; 0656 } 0657 d->token = QByteArray(maybeToken.first, maybeToken.second); 0658 0659 // complain if trailing garbage is found: 0660 eatCFWS(scursor, send, isCRLF); 0661 if (scursor != send) { 0662 KMIME_WARN << "trailing garbage after token in header allowing " 0663 "only a single token!" 0664 << Qt::endl; 0665 } 0666 return true; 0667 } 0668 0669 //-----</Token>------------------------- 0670 0671 //-----<PhraseList>------------------------- 0672 0673 //@cond PRIVATE 0674 kmime_mk_trivial_ctor_with_dptr(PhraseList, Structured) 0675 //@endcond 0676 0677 QByteArray PhraseList::as7BitString(bool withHeaderType) const 0678 { 0679 const Q_D(PhraseList); 0680 if (isEmpty()) { 0681 return {}; 0682 } 0683 0684 QByteArray rv; 0685 if (withHeaderType) { 0686 rv = typeIntro(); 0687 } 0688 0689 for (int i = 0; i < d->phraseList.count(); ++i) { 0690 // FIXME: only encode when needed, quote when needed, etc. 0691 rv += encodeRFC2047String(d->phraseList[i], d->encCS, false, false); 0692 if (i != d->phraseList.count() - 1) { 0693 rv += ", "; 0694 } 0695 } 0696 0697 return rv; 0698 } 0699 0700 QString PhraseList::asUnicodeString() const 0701 { 0702 return d_func()->phraseList.join(QLatin1String(", ")); 0703 } 0704 0705 void PhraseList::clear() 0706 { 0707 Q_D(PhraseList); 0708 d->phraseList.clear(); 0709 } 0710 0711 bool PhraseList::isEmpty() const 0712 { 0713 return d_func()->phraseList.isEmpty(); 0714 } 0715 0716 QStringList PhraseList::phrases() const 0717 { 0718 return d_func()->phraseList; 0719 } 0720 0721 bool PhraseList::parse(const char *&scursor, const char *const send, 0722 bool isCRLF) 0723 { 0724 Q_D(PhraseList); 0725 d->phraseList.clear(); 0726 0727 while (scursor != send) { 0728 eatCFWS(scursor, send, isCRLF); 0729 // empty entry ending the list: OK. 0730 if (scursor == send) { 0731 return true; 0732 } 0733 // empty entry: ignore. 0734 if (*scursor == ',') { 0735 scursor++; 0736 continue; 0737 } 0738 0739 QString maybePhrase; 0740 if (!parsePhrase(scursor, send, maybePhrase, isCRLF)) { 0741 return false; 0742 } 0743 d->phraseList.append(maybePhrase); 0744 0745 eatCFWS(scursor, send, isCRLF); 0746 // non-empty entry ending the list: OK. 0747 if (scursor == send) { 0748 return true; 0749 } 0750 // comma separating the phrases: eat. 0751 if (*scursor == ',') { 0752 scursor++; 0753 } 0754 } 0755 return true; 0756 } 0757 0758 //-----</PhraseList>------------------------- 0759 0760 //-----<DotAtom>------------------------- 0761 0762 //@cond PRIVATE 0763 kmime_mk_trivial_ctor_with_dptr(DotAtom, Structured) 0764 //@endcond 0765 0766 QByteArray DotAtom::as7BitString(bool withHeaderType) const 0767 { 0768 if (isEmpty()) { 0769 return {}; 0770 } 0771 0772 QByteArray rv; 0773 if (withHeaderType) { 0774 rv += typeIntro(); 0775 } 0776 0777 rv += d_func()->dotAtom; 0778 return rv; 0779 } 0780 0781 QString DotAtom::asUnicodeString() const 0782 { 0783 return QString::fromLatin1(d_func()->dotAtom); 0784 } 0785 0786 void DotAtom::clear() 0787 { 0788 Q_D(DotAtom); 0789 d->dotAtom.clear(); 0790 } 0791 0792 bool DotAtom::isEmpty() const 0793 { 0794 return d_func()->dotAtom.isEmpty(); 0795 } 0796 0797 bool DotAtom::parse(const char *&scursor, const char *const send, 0798 bool isCRLF) 0799 { 0800 Q_D(DotAtom); 0801 QByteArray maybeDotAtom; 0802 if (!parseDotAtom(scursor, send, maybeDotAtom, isCRLF)) { 0803 return false; 0804 } 0805 0806 d->dotAtom = maybeDotAtom; 0807 0808 eatCFWS(scursor, send, isCRLF); 0809 if (scursor != send) { 0810 KMIME_WARN << "trailing garbage after dot-atom in header allowing " 0811 "only a single dot-atom!" 0812 << Qt::endl; 0813 } 0814 return true; 0815 } 0816 0817 //-----</DotAtom>------------------------- 0818 0819 //-----<Parametrized>------------------------- 0820 0821 //@cond PRIVATE 0822 kmime_mk_trivial_ctor_with_dptr(Parametrized, Structured) 0823 kmime_mk_dptr_ctor(Parametrized, Structured) 0824 //@endcond 0825 0826 QByteArray Parametrized::as7BitString(bool withHeaderType) const 0827 { 0828 const Q_D(Parametrized); 0829 if (isEmpty()) { 0830 return {}; 0831 } 0832 0833 QByteArray rv; 0834 if (withHeaderType) { 0835 rv += typeIntro(); 0836 } 0837 0838 bool first = true; 0839 for (QMap<QString, QString>::ConstIterator it = d->parameterHash.constBegin(); 0840 it != d->parameterHash.constEnd(); ++it) { 0841 if (!first) { 0842 rv += "; "; 0843 } else { 0844 first = false; 0845 } 0846 if (isUsAscii(it.value())) { 0847 rv += it.key().toLatin1() + '='; 0848 QByteArray tmp = it.value().toLatin1(); 0849 addQuotes(tmp, true); // force quoting, eg. for whitespaces in parameter value 0850 rv += tmp; 0851 } else { 0852 if (useOutlookAttachmentEncoding()) { 0853 rv += it.key().toLatin1() + '='; 0854 qCDebug(KMIME_LOG) << "doing:" << it.value() << QLatin1String(d->encCS); 0855 rv += "\"" + encodeRFC2047String(it.value(), d->encCS) + "\""; 0856 } else { 0857 rv += it.key().toLatin1() + "*="; 0858 rv += encodeRFC2231String(it.value(), d->encCS); 0859 } 0860 } 0861 } 0862 0863 return rv; 0864 } 0865 0866 QString Parametrized::parameter(const QString &key) const 0867 { 0868 return d_func()->parameterHash.value(key.toLower()); 0869 } 0870 0871 bool Parametrized::hasParameter(const QString &key) const 0872 { 0873 return d_func()->parameterHash.contains(key.toLower()); 0874 } 0875 0876 void Parametrized::setParameter(const QString &key, const QString &value) 0877 { 0878 Q_D(Parametrized); 0879 d->parameterHash.insert(key.toLower(), value); 0880 } 0881 0882 bool Parametrized::isEmpty() const 0883 { 0884 return d_func()->parameterHash.isEmpty(); 0885 } 0886 0887 void Parametrized::clear() 0888 { 0889 Q_D(Parametrized); 0890 d->parameterHash.clear(); 0891 } 0892 0893 bool Parametrized::parse(const char *&scursor, const char *const send, 0894 bool isCRLF) 0895 { 0896 Q_D(Parametrized); 0897 d->parameterHash.clear(); 0898 QByteArray charset; 0899 if (!parseParameterListWithCharset(scursor, send, d->parameterHash, charset, isCRLF)) { 0900 return false; 0901 } 0902 d->encCS = charset; 0903 return true; 0904 } 0905 0906 //-----</Parametrized>------------------------- 0907 0908 //-----<Ident>------------------------- 0909 0910 //@cond PRIVATE 0911 kmime_mk_trivial_ctor_with_dptr(Ident, Address) 0912 kmime_mk_dptr_ctor(Ident, Address) 0913 //@endcond 0914 0915 QByteArray Ident::as7BitString(bool withHeaderType) const 0916 { 0917 const Q_D(Ident); 0918 if (d->msgIdList.isEmpty()) { 0919 return {}; 0920 } 0921 0922 QByteArray rv; 0923 if (withHeaderType) { 0924 rv = typeIntro(); 0925 } 0926 for (const Types::AddrSpec &addr : std::as_const(d->msgIdList)) { 0927 if (!addr.isEmpty()) { 0928 const QString asString = addr.asString(); 0929 rv += '<'; 0930 if (!asString.isEmpty()) { 0931 rv += asString.toLatin1(); // FIXME: change parsing to use QByteArrays 0932 } 0933 rv += "> "; 0934 } 0935 } 0936 if (!rv.isEmpty()) { 0937 rv.resize(rv.length() - 1); 0938 } 0939 return rv; 0940 } 0941 0942 void Ident::clear() 0943 { 0944 Q_D(Ident); 0945 d->msgIdList.clear(); 0946 d->cachedIdentifier.clear(); 0947 } 0948 0949 bool Ident::isEmpty() const 0950 { 0951 return d_func()->msgIdList.isEmpty(); 0952 } 0953 0954 bool Ident::parse(const char *&scursor, const char *const send, bool isCRLF) 0955 { 0956 Q_D(Ident); 0957 // msg-id := "<" id-left "@" id-right ">" 0958 // id-left := dot-atom-text / no-fold-quote / local-part 0959 // id-right := dot-atom-text / no-fold-literal / domain 0960 // 0961 // equivalent to: 0962 // msg-id := angle-addr 0963 0964 d->msgIdList.clear(); 0965 d->cachedIdentifier.clear(); 0966 0967 while (scursor != send) { 0968 eatCFWS(scursor, send, isCRLF); 0969 // empty entry ending the list: OK. 0970 if (scursor == send) { 0971 return true; 0972 } 0973 // empty entry: ignore. 0974 if (*scursor == ',') { 0975 scursor++; 0976 continue; 0977 } 0978 0979 AddrSpec maybeMsgId; 0980 if (!parseAngleAddr(scursor, send, maybeMsgId, isCRLF)) { 0981 return false; 0982 } 0983 d->msgIdList.append(maybeMsgId); 0984 0985 eatCFWS(scursor, send, isCRLF); 0986 // header end ending the list: OK. 0987 if (scursor == send) { 0988 return true; 0989 } 0990 // regular item separator: eat it. 0991 if (*scursor == ',') { 0992 scursor++; 0993 } 0994 } 0995 return true; 0996 } 0997 0998 QVector<QByteArray> Ident::identifiers() const 0999 { 1000 QVector<QByteArray> rv; 1001 const auto msgIdList = d_func()->msgIdList; 1002 for (const Types::AddrSpec &addr : msgIdList) { 1003 if (!addr.isEmpty()) { 1004 const QString asString = addr.asString(); 1005 if (!asString.isEmpty()) { 1006 rv.append(asString.toLatin1()); // FIXME: change parsing to use QByteArrays 1007 } 1008 } 1009 } 1010 return rv; 1011 } 1012 1013 void Ident::fromIdent(const Ident* ident) 1014 { 1015 d_func()->encCS = ident->d_func()->encCS; 1016 d_func()->msgIdList = ident->d_func()->msgIdList; 1017 d_func()->cachedIdentifier = ident->d_func()->cachedIdentifier; 1018 } 1019 1020 void Ident::appendIdentifier(const QByteArray &id) 1021 { 1022 Q_D(Ident); 1023 QByteArray tmp = id; 1024 if (!tmp.startsWith('<')) { 1025 tmp.prepend('<'); 1026 } 1027 if (!tmp.endsWith('>')) { 1028 tmp.append('>'); 1029 } 1030 AddrSpec msgId; 1031 const char *cursor = tmp.constData(); 1032 if (parseAngleAddr(cursor, cursor + tmp.length(), msgId)) { 1033 d->msgIdList.append(msgId); 1034 } else { 1035 qCWarning(KMIME_LOG) << "Unable to parse address spec!"; 1036 } 1037 } 1038 1039 //-----</Ident>------------------------- 1040 1041 //-----<SingleIdent>------------------------- 1042 1043 //@cond PRIVATE 1044 kmime_mk_trivial_ctor_with_dptr(SingleIdent, Ident) 1045 kmime_mk_dptr_ctor(SingleIdent, Ident) 1046 //@endcond 1047 1048 QByteArray SingleIdent::identifier() const 1049 { 1050 if (d_func()->msgIdList.isEmpty()) { 1051 return {}; 1052 } 1053 1054 if (d_func()->cachedIdentifier.isEmpty()) { 1055 const Types::AddrSpec &addr = d_func()->msgIdList.first(); 1056 if (!addr.isEmpty()) { 1057 const QString asString = addr.asString(); 1058 if (!asString.isEmpty()) { 1059 d_func()->cachedIdentifier = asString.toLatin1();// FIXME: change parsing to use QByteArrays 1060 } 1061 } 1062 } 1063 1064 return d_func()->cachedIdentifier; 1065 } 1066 1067 void SingleIdent::setIdentifier(const QByteArray &id) 1068 { 1069 Q_D(SingleIdent); 1070 d->msgIdList.clear(); 1071 d->cachedIdentifier.clear(); 1072 appendIdentifier(id); 1073 } 1074 1075 bool SingleIdent::parse(const char *&scursor, const char *const send, 1076 bool isCRLF) 1077 { 1078 Q_D(SingleIdent); 1079 if (!Ident::parse(scursor, send, isCRLF)) { 1080 return false; 1081 } 1082 1083 if (d->msgIdList.count() > 1) { 1084 KMIME_WARN << "more than one msg-id in header " 1085 << "allowing only a single one!" 1086 << Qt::endl; 1087 } 1088 return true; 1089 } 1090 1091 //-----</SingleIdent>------------------------- 1092 1093 } // namespace Generics 1094 1095 //-----<ReturnPath>------------------------- 1096 1097 //@cond PRIVATE 1098 kmime_mk_trivial_ctor_with_name_and_dptr(ReturnPath, Generics::Address, Return-Path) 1099 //@endcond 1100 1101 QByteArray ReturnPath::as7BitString(bool withHeaderType) const 1102 { 1103 if (isEmpty()) { 1104 return {}; 1105 } 1106 1107 QByteArray rv; 1108 if (withHeaderType) { 1109 rv += typeIntro(); 1110 } 1111 rv += '<' + d_func()->mailbox.as7BitString(d_func()->encCS) + '>'; 1112 return rv; 1113 } 1114 1115 void ReturnPath::clear() 1116 { 1117 Q_D(ReturnPath); 1118 d->mailbox.setAddress(Types::AddrSpec()); 1119 d->mailbox.setName(QString()); 1120 } 1121 1122 bool ReturnPath::isEmpty() const 1123 { 1124 const Q_D(ReturnPath); 1125 return !d->mailbox.hasAddress() && !d->mailbox.hasName(); 1126 } 1127 1128 bool ReturnPath::parse(const char *&scursor, const char *const send, 1129 bool isCRLF) 1130 { 1131 Q_D(ReturnPath); 1132 eatCFWS(scursor, send, isCRLF); 1133 if (scursor == send) { 1134 return false; 1135 } 1136 1137 const char *oldscursor = scursor; 1138 1139 Mailbox maybeMailbox; 1140 if (!parseMailbox(scursor, send, maybeMailbox, isCRLF)) { 1141 // mailbox parsing failed, but check for empty brackets: 1142 scursor = oldscursor; 1143 if (*scursor != '<') { 1144 return false; 1145 } 1146 scursor++; 1147 eatCFWS(scursor, send, isCRLF); 1148 if (scursor == send || *scursor != '>') { 1149 return false; 1150 } 1151 scursor++; 1152 1153 // prepare a Null mailbox: 1154 AddrSpec emptyAddrSpec; 1155 maybeMailbox.setName(QString()); 1156 maybeMailbox.setAddress(emptyAddrSpec); 1157 } else { 1158 // check that there was no display-name: 1159 if (maybeMailbox.hasName()) { 1160 KMIME_WARN << "display-name \"" << maybeMailbox.name() 1161 << "\" in Return-Path!" << Qt::endl; 1162 } 1163 } 1164 d->mailbox = maybeMailbox; 1165 1166 // see if that was all: 1167 eatCFWS(scursor, send, isCRLF); 1168 // and warn if it wasn't: 1169 if (scursor != send) { 1170 KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" 1171 << Qt::endl; 1172 } 1173 return true; 1174 } 1175 1176 //-----</ReturnPath>------------------------- 1177 1178 //-----<Generic>------------------------------- 1179 1180 // NOTE: Do *not* register Generic with HeaderFactory, since its type() is changeable. 1181 1182 Generic::Generic() : Generics::Unstructured(new GenericPrivate) 1183 { 1184 } 1185 1186 Generic::Generic(const char *t, int len) : Generics::Unstructured(new GenericPrivate) 1187 { 1188 setType(t, len); 1189 } 1190 1191 Generic::~Generic() 1192 { 1193 Q_D(Generic); 1194 delete d; 1195 d_ptr = nullptr; 1196 } 1197 1198 void Generic::clear() 1199 { 1200 Q_D(Generic); 1201 delete[] d->type; 1202 d->type = nullptr; 1203 Unstructured::clear(); 1204 } 1205 1206 bool Generic::isEmpty() const 1207 { 1208 return d_func()->type == nullptr || Unstructured::isEmpty(); 1209 } 1210 1211 const char *Generic::type() const 1212 { 1213 return d_func()->type; 1214 } 1215 1216 void Generic::setType(const char *type, int len) 1217 { 1218 Q_D(Generic); 1219 if (d->type) { 1220 delete[] d->type; 1221 } 1222 if (type) { 1223 const int l = (len < 0 ? strlen(type) : len) + 1; 1224 d->type = new char[l]; 1225 qstrncpy(d->type, type, l); 1226 } else { 1227 d->type = nullptr; 1228 } 1229 } 1230 1231 //-----<Generic>------------------------------- 1232 1233 //-----<MessageID>----------------------------- 1234 1235 //@cond PRIVATE 1236 kmime_mk_trivial_ctor_with_name(MessageID, Generics::SingleIdent, Message-ID) 1237 //@endcond 1238 1239 void MessageID::generate(const QByteArray &fqdn) 1240 { 1241 setIdentifier('<' + uniqueString() + '@' + fqdn + '>'); 1242 } 1243 1244 //-----</MessageID>---------------------------- 1245 1246 //-----<Control>------------------------------- 1247 1248 //@cond PRIVATE 1249 kmime_mk_trivial_ctor_with_name_and_dptr(Control, Generics::Structured, Control) 1250 //@endcond 1251 1252 QByteArray Control::as7BitString(bool withHeaderType) const 1253 { 1254 const Q_D(Control); 1255 if (isEmpty()) { 1256 return {}; 1257 } 1258 1259 QByteArray rv; 1260 if (withHeaderType) { 1261 rv += typeIntro(); 1262 } 1263 1264 rv += d->name; 1265 if (!d->parameter.isEmpty()) { 1266 rv += ' ' + d->parameter; 1267 } 1268 return rv; 1269 } 1270 1271 void Control::clear() 1272 { 1273 Q_D(Control); 1274 d->name.clear(); 1275 d->parameter.clear(); 1276 } 1277 1278 bool Control::isEmpty() const 1279 { 1280 return d_func()->name.isEmpty(); 1281 } 1282 1283 QByteArray Control::controlType() const 1284 { 1285 return d_func()->name; 1286 } 1287 1288 QByteArray Control::parameter() const 1289 { 1290 return d_func()->parameter; 1291 } 1292 1293 bool Control::isCancel() const 1294 { 1295 return d_func()->name.toLower() == "cancel"; 1296 } 1297 1298 void Control::setCancel(const QByteArray &msgid) 1299 { 1300 Q_D(Control); 1301 d->name = "cancel"; 1302 d->parameter = msgid; 1303 } 1304 1305 bool Control::parse(const char *&scursor, const char *const send, bool isCRLF) 1306 { 1307 Q_D(Control); 1308 clear(); 1309 eatCFWS(scursor, send, isCRLF); 1310 if (scursor == send) { 1311 return false; 1312 } 1313 const char *start = scursor; 1314 while (scursor != send && !isspace(*scursor)) { 1315 ++scursor; 1316 } 1317 d->name = QByteArray(start, scursor - start); 1318 eatCFWS(scursor, send, isCRLF); 1319 d->parameter = QByteArray(scursor, send - scursor); 1320 return true; 1321 } 1322 1323 //-----</Control>------------------------------ 1324 1325 //-----<MailCopiesTo>-------------------------- 1326 1327 //@cond PRIVATE 1328 kmime_mk_trivial_ctor_with_name_and_dptr(MailCopiesTo, 1329 Generics::AddressList, Mail-Copies-To) 1330 //@endcond 1331 1332 QByteArray MailCopiesTo::as7BitString(bool withHeaderType) const 1333 { 1334 QByteArray rv; 1335 if (withHeaderType) { 1336 rv += typeIntro(); 1337 } 1338 if (!AddressList::isEmpty()) { 1339 rv += AddressList::as7BitString(false); 1340 } else { 1341 if (d_func()->alwaysCopy) { 1342 rv += "poster"; 1343 } else if (d_func()->neverCopy) { 1344 rv += "nobody"; 1345 } 1346 } 1347 return rv; 1348 } 1349 1350 QString MailCopiesTo::asUnicodeString() const 1351 { 1352 if (!AddressList::isEmpty()) { 1353 return AddressList::asUnicodeString(); 1354 } 1355 if (d_func()->alwaysCopy) { 1356 return QStringLiteral("poster"); 1357 } 1358 if (d_func()->neverCopy) { 1359 return QStringLiteral("nobody"); 1360 } 1361 return {}; 1362 } 1363 1364 void MailCopiesTo::clear() 1365 { 1366 Q_D(MailCopiesTo); 1367 AddressList::clear(); 1368 d->alwaysCopy = false; 1369 d->neverCopy = false; 1370 } 1371 1372 bool MailCopiesTo::isEmpty() const 1373 { 1374 return AddressList::isEmpty() && !(d_func()->alwaysCopy || d_func()->neverCopy); 1375 } 1376 1377 bool MailCopiesTo::alwaysCopy() const 1378 { 1379 return !AddressList::isEmpty() || d_func()->alwaysCopy; 1380 } 1381 1382 void MailCopiesTo::setAlwaysCopy() 1383 { 1384 Q_D(MailCopiesTo); 1385 clear(); 1386 d->alwaysCopy = true; 1387 } 1388 1389 bool MailCopiesTo::neverCopy() const 1390 { 1391 return d_func()->neverCopy; 1392 } 1393 1394 void MailCopiesTo::setNeverCopy() 1395 { 1396 Q_D(MailCopiesTo); 1397 clear(); 1398 d->neverCopy = true; 1399 } 1400 1401 bool MailCopiesTo::parse(const char *&scursor, const char *const send, 1402 bool isCRLF) 1403 { 1404 Q_D(MailCopiesTo); 1405 clear(); 1406 if (send - scursor == 5) { 1407 if (qstrnicmp("never", scursor, 5) == 0) { 1408 d->neverCopy = true; 1409 return true; 1410 } 1411 } 1412 if (send - scursor == 6) { 1413 if (qstrnicmp("always", scursor, 6) == 0 || qstrnicmp("poster", scursor, 6) == 0) { 1414 d->alwaysCopy = true; 1415 return true; 1416 } 1417 if (qstrnicmp("nobody", scursor, 6) == 0) { 1418 d->neverCopy = true; 1419 return true; 1420 } 1421 } 1422 return AddressList::parse(scursor, send, isCRLF); 1423 } 1424 1425 //-----</MailCopiesTo>------------------------- 1426 1427 //-----<Date>---------------------------------- 1428 1429 //@cond PRIVATE 1430 kmime_mk_trivial_ctor_with_name_and_dptr(Date, Generics::Structured, Date) 1431 //@endcond 1432 1433 QByteArray Date::as7BitString(bool withHeaderType) const 1434 { 1435 if (isEmpty()) { 1436 return {}; 1437 } 1438 1439 QByteArray rv; 1440 if (withHeaderType) { 1441 rv += typeIntro(); 1442 } 1443 //QT5 fix port to QDateTime Qt::RFC2822Date is not enough we need to fix it. We need to use QLocale("C") + add "ddd, "; 1444 //rv += d_func()->dateTime.toString( Qt::RFC2822Date ).toLatin1(); 1445 rv += QLocale::c().toString(d_func()->dateTime, QStringLiteral("ddd, ")).toLatin1(); 1446 rv += d_func()->dateTime.toString(Qt::RFC2822Date).toLatin1(); 1447 1448 return rv; 1449 } 1450 1451 void Date::clear() { 1452 Q_D(Date); 1453 d->dateTime = QDateTime(); 1454 } 1455 1456 bool Date::isEmpty() const { 1457 return d_func()->dateTime.isNull() || !d_func()->dateTime.isValid(); 1458 } 1459 1460 QDateTime Date::dateTime() const { 1461 return d_func()->dateTime; 1462 } 1463 1464 void Date::setDateTime(const QDateTime & dt) { 1465 Q_D(Date); 1466 d->dateTime = dt; 1467 } 1468 1469 int Date::ageInDays() const { 1470 QDate today = QDate::currentDate(); 1471 return dateTime().date().daysTo(today); 1472 } 1473 1474 bool Date::parse(const char *&scursor, const char *const send, bool isCRLF) { 1475 Q_D(Date); 1476 return parseDateTime(scursor, send, d->dateTime, isCRLF); 1477 } 1478 1479 //-----</Date>--------------------------------- 1480 1481 //-----<Newsgroups>---------------------------- 1482 1483 //@cond PRIVATE 1484 kmime_mk_trivial_ctor_with_name_and_dptr(Newsgroups, Generics::Structured, Newsgroups) 1485 kmime_mk_trivial_ctor_with_name(FollowUpTo, Newsgroups, Followup-To) 1486 //@endcond 1487 1488 QByteArray Newsgroups::as7BitString(bool withHeaderType) const { 1489 const Q_D(Newsgroups); 1490 if (isEmpty()) { 1491 return {}; 1492 } 1493 1494 QByteArray rv; 1495 if (withHeaderType) { 1496 rv += typeIntro(); 1497 } 1498 1499 for (int i = 0; i < d->groups.count(); ++i) { 1500 rv += d->groups[ i ]; 1501 if (i != d->groups.count() - 1) { 1502 rv += ','; 1503 } 1504 } 1505 return rv; 1506 } 1507 1508 void Newsgroups::fromUnicodeString(const QString & s, const QByteArray & b) { 1509 Q_UNUSED(b) 1510 Q_D(Newsgroups); 1511 from7BitString(s.toUtf8()); 1512 d->encCS = cachedCharset("UTF-8"); 1513 } 1514 1515 QString Newsgroups::asUnicodeString() const { 1516 return QString::fromUtf8(as7BitString(false)); 1517 } 1518 1519 void Newsgroups::clear() { 1520 Q_D(Newsgroups); 1521 d->groups.clear(); 1522 } 1523 1524 bool Newsgroups::isEmpty() const { 1525 return d_func()->groups.isEmpty(); 1526 } 1527 1528 QVector<QByteArray> Newsgroups::groups() const { 1529 return d_func()->groups; 1530 } 1531 1532 void Newsgroups::setGroups(const QVector<QByteArray> &groups) { 1533 Q_D(Newsgroups); 1534 d->groups = groups; 1535 } 1536 1537 bool Newsgroups::isCrossposted() const { 1538 return d_func()->groups.count() >= 2; 1539 } 1540 1541 bool Newsgroups::parse(const char *&scursor, const char *const send, bool isCRLF) { 1542 Q_D(Newsgroups); 1543 clear(); 1544 while (true) { 1545 eatCFWS(scursor, send, isCRLF); 1546 if (scursor != send && *scursor == ',') { 1547 ++scursor; 1548 } 1549 eatCFWS(scursor, send, isCRLF); 1550 if (scursor == send) { 1551 return true; 1552 } 1553 const char *start = scursor; 1554 while (scursor != send && !isspace(*scursor) && *scursor != ',') { 1555 ++scursor; 1556 } 1557 QByteArray group(start, scursor - start); 1558 d->groups.append(group); 1559 } 1560 return true; 1561 } 1562 1563 //-----</Newsgroups>--------------------------- 1564 1565 //-----<Lines>--------------------------------- 1566 1567 //@cond PRIVATE 1568 kmime_mk_trivial_ctor_with_name_and_dptr(Lines, Generics::Structured, Lines) 1569 //@endcond 1570 1571 QByteArray Lines::as7BitString(bool withHeaderType) const { 1572 if (isEmpty()) { 1573 return {}; 1574 } 1575 1576 QByteArray num; 1577 num.setNum(d_func()->lines); 1578 1579 if (withHeaderType) { 1580 return typeIntro() + num; 1581 } 1582 return num; 1583 } 1584 1585 QString Lines::asUnicodeString() const { 1586 if (isEmpty()) { 1587 return {}; 1588 } 1589 return QString::number(d_func()->lines); 1590 } 1591 1592 void Lines::clear() { 1593 Q_D(Lines); 1594 d->lines = -1; 1595 } 1596 1597 bool Lines::isEmpty() const { 1598 return d_func()->lines == -1; 1599 } 1600 1601 int Lines::numberOfLines() const { 1602 return d_func()->lines; 1603 } 1604 1605 void Lines::setNumberOfLines(int lines) { 1606 Q_D(Lines); 1607 d->lines = lines; 1608 } 1609 1610 bool Lines::parse(const char *&scursor, const char *const send, bool isCRLF) { 1611 Q_D(Lines); 1612 eatCFWS(scursor, send, isCRLF); 1613 if (parseDigits(scursor, send, d->lines) == 0) { 1614 clear(); 1615 return false; 1616 } 1617 return true; 1618 } 1619 1620 //-----</Lines>-------------------------------- 1621 1622 //-----<Content-Type>-------------------------- 1623 1624 //@cond PRIVATE 1625 kmime_mk_trivial_ctor_with_name_and_dptr(ContentType, Generics::Parametrized, 1626 Content-Type) 1627 //@endcond 1628 1629 bool ContentType::isEmpty() const { 1630 return d_func()->mimeType.isEmpty(); 1631 } 1632 1633 void ContentType::clear() { 1634 Q_D(ContentType); 1635 d->category = CCsingle; 1636 d->mimeType.clear(); 1637 Parametrized::clear(); 1638 } 1639 1640 QByteArray ContentType::as7BitString(bool withHeaderType) const { 1641 if (isEmpty()) { 1642 return {}; 1643 } 1644 1645 QByteArray rv; 1646 if (withHeaderType) { 1647 rv += typeIntro(); 1648 } 1649 1650 rv += mimeType(); 1651 if (!Parametrized::isEmpty()) { 1652 rv += "; " + Parametrized::as7BitString(false); 1653 } 1654 1655 return rv; 1656 } 1657 1658 QByteArray ContentType::mimeType() const { 1659 Q_D(const ContentType); 1660 return d->mimeType; 1661 } 1662 1663 QByteArray ContentType::mediaType() const { 1664 Q_D(const ContentType); 1665 const int pos = d->mimeType.indexOf('/'); 1666 if (pos < 0) { 1667 return d->mimeType; 1668 } else { 1669 return d->mimeType.left(pos); 1670 } 1671 } 1672 1673 QByteArray ContentType::subType() const { 1674 Q_D(const ContentType); 1675 const int pos = d->mimeType.indexOf('/'); 1676 if (pos < 0) { 1677 return {}; 1678 } else { 1679 return d->mimeType.mid(pos + 1); 1680 } 1681 } 1682 1683 void ContentType::setMimeType(const QByteArray & mimeType) { 1684 Q_D(ContentType); 1685 d->mimeType = mimeType; 1686 1687 if (isMultipart()) { 1688 d->category = CCcontainer; 1689 } else { 1690 d->category = CCsingle; 1691 } 1692 } 1693 1694 bool ContentType::isMediatype(const char *mediatype) const { 1695 Q_D(const ContentType); 1696 const int len = strlen(mediatype); 1697 return qstrnicmp(d->mimeType.constData(), mediatype, len) == 0 && 1698 (d->mimeType.at(len) == '/' || d->mimeType.size() == len); 1699 } 1700 1701 bool ContentType::isSubtype(const char *subtype) const { 1702 Q_D(const ContentType); 1703 const int pos = d->mimeType.indexOf('/'); 1704 if (pos < 0) { 1705 return false; 1706 } 1707 const int len = strlen(subtype); 1708 return qstrnicmp(d->mimeType.constData() + pos + 1, subtype, len) == 0 && 1709 d->mimeType.size() == pos + len + 1; 1710 } 1711 1712 bool ContentType::isMimeType(const char* mimeType) const 1713 { 1714 Q_D(const ContentType); 1715 return qstricmp(d->mimeType.constData(), mimeType) == 0; 1716 } 1717 1718 bool ContentType::isText() const { 1719 return (isMediatype("text") || isEmpty()); 1720 } 1721 1722 bool ContentType::isPlainText() const { 1723 return (qstricmp(d_func()->mimeType.constData(), "text/plain") == 0 || isEmpty()); 1724 } 1725 1726 bool ContentType::isHTMLText() const { 1727 return qstricmp(d_func()->mimeType.constData(), "text/html") == 0; 1728 } 1729 1730 bool ContentType::isImage() const { 1731 return isMediatype("image"); 1732 } 1733 1734 bool ContentType::isMultipart() const { 1735 return isMediatype("multipart"); 1736 } 1737 1738 bool ContentType::isPartial() const { 1739 return qstricmp(d_func()->mimeType.constData(), "message/partial") == 0; 1740 } 1741 1742 QByteArray ContentType::charset() const { 1743 QByteArray ret = parameter(QStringLiteral("charset")).toLatin1(); 1744 if (ret.isEmpty()) { 1745 //return the default-charset if necessary 1746 ret = Content::defaultCharset(); 1747 } 1748 return ret; 1749 } 1750 1751 void ContentType::setCharset(const QByteArray & s) { 1752 setParameter(QStringLiteral("charset"), QString::fromLatin1(s)); 1753 } 1754 1755 QByteArray ContentType::boundary() const { 1756 return parameter(QStringLiteral("boundary")).toLatin1(); 1757 } 1758 1759 void ContentType::setBoundary(const QByteArray & s) { 1760 setParameter(QStringLiteral("boundary"), QString::fromLatin1(s)); 1761 } 1762 1763 QString ContentType::name() const { 1764 return parameter(QStringLiteral("name")); 1765 } 1766 1767 void ContentType::setName(const QString & s, const QByteArray & cs) { 1768 Q_D(ContentType); 1769 d->encCS = cs; 1770 setParameter(QStringLiteral("name"), s); 1771 } 1772 1773 QByteArray ContentType::id() const { 1774 return parameter(QStringLiteral("id")).toLatin1(); 1775 } 1776 1777 void ContentType::setId(const QByteArray & s) { 1778 setParameter(QStringLiteral("id"), QString::fromLatin1(s)); 1779 } 1780 1781 int ContentType::partialNumber() const { 1782 QByteArray p = parameter(QStringLiteral("number")).toLatin1(); 1783 if (!p.isEmpty()) { 1784 return p.toInt(); 1785 } else { 1786 return -1; 1787 } 1788 } 1789 1790 int ContentType::partialCount() const { 1791 QByteArray p = parameter(QStringLiteral("total")).toLatin1(); 1792 if (!p.isEmpty()) { 1793 return p.toInt(); 1794 } else { 1795 return -1; 1796 } 1797 } 1798 1799 contentCategory ContentType::category() const { 1800 return d_func()->category; 1801 } 1802 1803 void ContentType::setCategory(contentCategory c) { 1804 Q_D(ContentType); 1805 d->category = c; 1806 } 1807 1808 void ContentType::setPartialParams(int total, int number) { 1809 setParameter(QStringLiteral("number"), QString::number(number)); 1810 setParameter(QStringLiteral("total"), QString::number(total)); 1811 } 1812 1813 bool ContentType::parse(const char *&scursor, const char *const send, 1814 bool isCRLF) { 1815 Q_D(ContentType); 1816 // content-type: type "/" subtype *(";" parameter) 1817 clear(); 1818 eatCFWS(scursor, send, isCRLF); 1819 if (scursor == send) { 1820 return false; // empty header 1821 } 1822 1823 // type 1824 QPair<const char *, int> maybeMimeType; 1825 if (!parseToken(scursor, send, maybeMimeType, ParseTokenNoFlag)) { 1826 return false; 1827 } 1828 // subtype 1829 eatCFWS(scursor, send, isCRLF); 1830 if (scursor == send || *scursor != '/') { 1831 return false; 1832 } 1833 scursor++; 1834 eatCFWS(scursor, send, isCRLF); 1835 if (scursor == send) { 1836 return false; 1837 } 1838 QPair<const char *, int> maybeSubType; 1839 if (!parseToken(scursor, send, maybeSubType, ParseTokenNoFlag)) { 1840 return false; 1841 } 1842 1843 d->mimeType.reserve(maybeMimeType.second + maybeSubType.second + 1); 1844 d->mimeType = QByteArray(maybeMimeType.first, maybeMimeType.second).toLower() 1845 + '/' + QByteArray(maybeSubType.first, maybeSubType.second).toLower(); 1846 1847 // parameter list 1848 eatCFWS(scursor, send, isCRLF); 1849 if (scursor == send) { 1850 goto success; // no parameters 1851 } 1852 if (*scursor != ';') { 1853 return false; 1854 } 1855 scursor++; 1856 1857 if (!Parametrized::parse(scursor, send, isCRLF)) { 1858 return false; 1859 } 1860 1861 // adjust category 1862 success: 1863 if (isMultipart()) { 1864 d->category = CCcontainer; 1865 } else { 1866 d->category = CCsingle; 1867 } 1868 return true; 1869 } 1870 1871 //-----</Content-Type>------------------------- 1872 1873 //-----<ContentID>---------------------- 1874 1875 kmime_mk_trivial_ctor_with_name_and_dptr(ContentID, SingleIdent, Content-ID) 1876 kmime_mk_dptr_ctor(ContentID, SingleIdent) 1877 1878 bool ContentID::parse(const char *&scursor, const char *const send, bool isCRLF) { 1879 Q_D(ContentID); 1880 // Content-id := "<" contentid ">" 1881 // contentid := now whitespaces 1882 1883 const char *origscursor = scursor; 1884 if (!SingleIdent::parse(scursor, send, isCRLF)) { 1885 scursor = origscursor; 1886 d->msgIdList.clear(); 1887 d->cachedIdentifier.clear(); 1888 1889 while (scursor != send) { 1890 eatCFWS(scursor, send, isCRLF); 1891 // empty entry ending the list: OK. 1892 if (scursor == send) { 1893 return true; 1894 } 1895 // empty entry: ignore. 1896 if (*scursor == ',') { 1897 scursor++; 1898 continue; 1899 } 1900 1901 AddrSpec maybeContentId; 1902 // Almost parseAngleAddr 1903 if (scursor == send || *scursor != '<') { 1904 return false; 1905 } 1906 scursor++; // eat '<' 1907 1908 eatCFWS(scursor, send, isCRLF); 1909 if (scursor == send) { 1910 return false; 1911 } 1912 1913 // Save chars until '>'' 1914 QByteArray result; 1915 if (!parseDotAtom(scursor, send, result, false)) { 1916 return false; 1917 } 1918 1919 eatCFWS(scursor, send, isCRLF); 1920 if (scursor == send || *scursor != '>') { 1921 return false; 1922 } 1923 scursor++; 1924 // /Almost parseAngleAddr 1925 1926 maybeContentId.localPart = QString::fromLatin1(result); // FIXME: just use QByteArray instead of AddrSpec in msgIdList? 1927 d->msgIdList.append(maybeContentId); 1928 1929 eatCFWS(scursor, send, isCRLF); 1930 // header end ending the list: OK. 1931 if (scursor == send) { 1932 return true; 1933 } 1934 // regular item separator: eat it. 1935 if (*scursor == ',') { 1936 scursor++; 1937 } 1938 } 1939 return true; 1940 } else { 1941 return true; 1942 } 1943 } 1944 1945 //-----</ContentID>---------------------- 1946 1947 //-----<ContentTransferEncoding>---------------------------- 1948 1949 //@cond PRIVATE 1950 kmime_mk_trivial_ctor_with_name_and_dptr(ContentTransferEncoding, 1951 Generics::Token, Content-Transfer-Encoding) 1952 //@endcond 1953 1954 typedef struct { 1955 const char *s; 1956 int e; 1957 } encTableType; 1958 1959 static const encTableType encTable[] = { 1960 { "7Bit", CE7Bit }, 1961 { "8Bit", CE8Bit }, 1962 { "quoted-printable", CEquPr }, 1963 { "base64", CEbase64 }, 1964 { "x-uuencode", CEuuenc }, 1965 { "binary", CEbinary }, 1966 { nullptr, 0} 1967 }; 1968 1969 void ContentTransferEncoding::clear() { 1970 Q_D(ContentTransferEncoding); 1971 d->decoded = true; 1972 d->cte = CE7Bit; 1973 Token::clear(); 1974 } 1975 1976 contentEncoding ContentTransferEncoding::encoding() const { 1977 return d_func()->cte; 1978 } 1979 1980 void ContentTransferEncoding::setEncoding(contentEncoding e) { 1981 Q_D(ContentTransferEncoding); 1982 d->cte = e; 1983 1984 for (int i = 0; encTable[i].s != nullptr; ++i) { 1985 if (d->cte == encTable[i].e) { 1986 setToken(encTable[i].s); 1987 break; 1988 } 1989 } 1990 } 1991 1992 bool ContentTransferEncoding::isDecoded() const { 1993 return d_func()->decoded; 1994 } 1995 1996 void ContentTransferEncoding::setDecoded(bool decoded) { 1997 Q_D(ContentTransferEncoding); 1998 d->decoded = decoded; 1999 } 2000 2001 bool ContentTransferEncoding::needToEncode() const { 2002 const Q_D(ContentTransferEncoding); 2003 return d->decoded && (d->cte == CEquPr || d->cte == CEbase64); 2004 } 2005 2006 bool ContentTransferEncoding::parse(const char *&scursor, 2007 const char *const send, bool isCRLF) { 2008 Q_D(ContentTransferEncoding); 2009 clear(); 2010 if (!Token::parse(scursor, send, isCRLF)) { 2011 return false; 2012 } 2013 2014 // TODO: error handling in case of an unknown encoding? 2015 for (int i = 0; encTable[i].s != nullptr; ++i) { 2016 if (qstricmp(token().constData(), encTable[i].s) == 0) { 2017 d->cte = (contentEncoding)encTable[i].e; 2018 break; 2019 } 2020 } 2021 d->decoded = (d->cte == CE7Bit || d->cte == CE8Bit); 2022 return true; 2023 } 2024 2025 //-----</ContentTransferEncoding>--------------------------- 2026 2027 //-----<ContentDisposition>-------------------------- 2028 2029 //@cond PRIVATE 2030 kmime_mk_trivial_ctor_with_name_and_dptr(ContentDisposition, 2031 Generics::Parametrized, Content-Disposition) 2032 //@endcond 2033 2034 QByteArray ContentDisposition::as7BitString(bool withHeaderType) const { 2035 if (isEmpty()) { 2036 return {}; 2037 } 2038 2039 QByteArray rv; 2040 if (withHeaderType) { 2041 rv += typeIntro(); 2042 } 2043 2044 if (d_func()->disposition == CDattachment) { 2045 rv += "attachment"; 2046 } else if (d_func()->disposition == CDinline) { 2047 rv += "inline"; 2048 } else { 2049 return {}; 2050 } 2051 2052 if (!Parametrized::isEmpty()) { 2053 rv += "; " + Parametrized::as7BitString(false); 2054 } 2055 2056 return rv; 2057 } 2058 2059 bool ContentDisposition::isEmpty() const { 2060 return d_func()->disposition == CDInvalid; 2061 } 2062 2063 void ContentDisposition::clear() { 2064 Q_D(ContentDisposition); 2065 d->disposition = CDInvalid; 2066 Parametrized::clear(); 2067 } 2068 2069 contentDisposition ContentDisposition::disposition() const { 2070 return d_func()->disposition; 2071 } 2072 2073 void ContentDisposition::setDisposition(contentDisposition disp) { 2074 Q_D(ContentDisposition); 2075 d->disposition = disp; 2076 } 2077 2078 QString KMime::Headers::ContentDisposition::filename() const { 2079 return parameter(QStringLiteral("filename")); 2080 } 2081 2082 void ContentDisposition::setFilename(const QString & filename) { 2083 setParameter(QStringLiteral("filename"), filename); 2084 } 2085 2086 bool ContentDisposition::parse(const char *&scursor, const char *const send, 2087 bool isCRLF) { 2088 Q_D(ContentDisposition); 2089 clear(); 2090 2091 // token 2092 QByteArray token; 2093 eatCFWS(scursor, send, isCRLF); 2094 if (scursor == send) { 2095 return false; 2096 } 2097 2098 QPair<const char *, int> maybeToken; 2099 if (!parseToken(scursor, send, maybeToken, ParseTokenNoFlag)) { 2100 return false; 2101 } 2102 2103 token = QByteArray(maybeToken.first, maybeToken.second).toLower(); 2104 if (token == "inline") { 2105 d->disposition = CDinline; 2106 } else if (token == "attachment") { 2107 d->disposition = CDattachment; 2108 } else { 2109 return false; 2110 } 2111 2112 // parameter list 2113 eatCFWS(scursor, send, isCRLF); 2114 if (scursor == send) { 2115 return true; // no parameters 2116 } 2117 2118 if (*scursor != ';') { 2119 return false; 2120 } 2121 scursor++; 2122 2123 return Parametrized::parse(scursor, send, isCRLF); 2124 } 2125 2126 //-----</ContentDisposition>------------------------- 2127 2128 //@cond PRIVATE 2129 kmime_mk_trivial_ctor_with_name(Subject, Generics::Unstructured, Subject) 2130 //@endcond 2131 2132 Base *createHeader(const QByteArray & type) { 2133 return HeaderFactory::createHeader(type); 2134 } 2135 2136 //@cond PRIVATE 2137 kmime_mk_trivial_ctor_with_name(ContentDescription, 2138 Generics::Unstructured, Content-Description) 2139 kmime_mk_trivial_ctor_with_name(ContentLocation, 2140 Generics::Unstructured, Content-Location) 2141 kmime_mk_trivial_ctor_with_name(From, Generics::MailboxList, From) 2142 kmime_mk_trivial_ctor_with_name(Sender, Generics::SingleMailbox, Sender) 2143 kmime_mk_trivial_ctor_with_name(To, Generics::AddressList, To) 2144 kmime_mk_trivial_ctor_with_name(Cc, Generics::AddressList, Cc) 2145 kmime_mk_trivial_ctor_with_name(Bcc, Generics::AddressList, Bcc) 2146 kmime_mk_trivial_ctor_with_name(ReplyTo, Generics::AddressList, Reply-To) 2147 kmime_mk_trivial_ctor_with_name(Keywords, Generics::PhraseList, Keywords) 2148 kmime_mk_trivial_ctor_with_name(MIMEVersion, Generics::DotAtom, MIME-Version) 2149 kmime_mk_trivial_ctor_with_name(Supersedes, Generics::SingleIdent, Supersedes) 2150 kmime_mk_trivial_ctor_with_name(InReplyTo, Generics::Ident, In-Reply-To) 2151 kmime_mk_trivial_ctor_with_name(References, Generics::Ident, References) 2152 kmime_mk_trivial_ctor_with_name(Organization, Generics::Unstructured, Organization) 2153 kmime_mk_trivial_ctor_with_name(UserAgent, Generics::Unstructured, User-Agent) 2154 //@endcond 2155 2156 } // namespace Headers 2157 2158 } // namespace KMime