File indexing completed on 2024-04-21 16:06:19
0001 /* -*- c++ -*- 0002 kmime_header_types.cpp 0003 0004 KMime, the KDE Internet mail/usenet news message library. 0005 SPDX-FileCopyrightText: 2001-2002 Marc Mutz <mutz@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "kmime_types.h" 0011 #include "kmime_codecs_p.h" 0012 #include "kmime_header_parsing.h" 0013 #include "kmime_header_parsing_p.h" 0014 #include "kmime_util.h" 0015 #include "kmime_util_p.h" 0016 #include "kmime_debug.h" 0017 0018 #include <KCodecs> 0019 0020 #include <QStringList> 0021 #include <QUrl> 0022 0023 namespace KMime 0024 { 0025 0026 namespace Types 0027 { 0028 0029 // QUrl::fromAce is extremely expensive, so only use it when necessary. 0030 // Fortunately, the presence of IDNA is readily detected with a substring match... 0031 static inline QString QUrl_fromAce_wrapper(const QString &domain) 0032 { 0033 if (domain.contains(QLatin1StringView("xn--"))) { 0034 return QUrl::fromAce(domain.toLatin1()); 0035 } else { 0036 return domain; 0037 } 0038 } 0039 0040 static QString addr_spec_as_string(const AddrSpec &as, bool pretty) 0041 { 0042 if (as.isEmpty()) { 0043 return {}; 0044 } 0045 0046 static const QChar dotChar = QLatin1Char('.'); 0047 static const QChar backslashChar = QLatin1Char('\\'); 0048 static const QChar quoteChar = QLatin1Char('"'); 0049 0050 bool needsQuotes = false; 0051 QString result; 0052 result.reserve(as.localPart.length() + as.domain.length() + 1); 0053 for (int i = 0 ; i < as.localPart.length() ; ++i) { 0054 const QChar ch = as.localPart.at(i); 0055 if (ch == dotChar || isAText(ch.toLatin1())) { 0056 result += ch; 0057 } else { 0058 needsQuotes = true; 0059 if (ch == backslashChar || ch == quoteChar) { 0060 result += backslashChar; 0061 } 0062 result += ch; 0063 } 0064 } 0065 const QString dom = pretty ? QUrl_fromAce_wrapper(as.domain) : as.domain ; 0066 if (needsQuotes) { 0067 result = quoteChar + result + quoteChar; 0068 } 0069 if (dom.isEmpty()) { 0070 return result; 0071 } else { 0072 result += QLatin1Char('@'); 0073 result += dom; 0074 return result; 0075 } 0076 } 0077 0078 QString AddrSpec::asString() const 0079 { 0080 return addr_spec_as_string(*this, false); 0081 } 0082 0083 QString AddrSpec::asPrettyString() const 0084 { 0085 return addr_spec_as_string(*this, true); 0086 } 0087 0088 bool AddrSpec::isEmpty() const 0089 { 0090 return localPart.isEmpty() && domain.isEmpty(); 0091 } 0092 0093 QByteArray Mailbox::address() const 0094 { 0095 QByteArray result; 0096 const QString asString = addr_spec_as_string(mAddrSpec, false); 0097 if (!asString.isEmpty()) { 0098 result = asString.toLatin1(); 0099 } 0100 return result; 0101 //return mAddrSpec.asString().toLatin1(); 0102 } 0103 0104 AddrSpec Mailbox::addrSpec() const 0105 { 0106 return mAddrSpec; 0107 } 0108 0109 QString Mailbox::name() const 0110 { 0111 return mDisplayName; 0112 } 0113 0114 void Mailbox::setAddress(const AddrSpec &addr) 0115 { 0116 mAddrSpec = addr; 0117 } 0118 0119 void Mailbox::setAddress(const QByteArray &addr) 0120 { 0121 const char *cursor = addr.constData(); 0122 if (!HeaderParsing::parseAngleAddr(cursor, 0123 cursor + addr.length(), mAddrSpec)) { 0124 if (!HeaderParsing::parseAddrSpec(cursor, cursor + addr.length(), 0125 mAddrSpec)) { 0126 qCWarning(KMIME_LOG) << "Mailbox: Invalid address"; 0127 return; 0128 } 0129 } 0130 } 0131 0132 void Mailbox::setName(const QString &name) 0133 { 0134 mDisplayName = removeBidiControlChars(name); 0135 } 0136 0137 void Mailbox::setNameFrom7Bit(const QByteArray &name, 0138 const QByteArray &defaultCharset) 0139 { 0140 QByteArray cs; 0141 setName(KCodecs::decodeRFC2047String(name, &cs, defaultCharset)); 0142 } 0143 0144 bool Mailbox::hasAddress() const 0145 { 0146 return !mAddrSpec.isEmpty(); 0147 } 0148 0149 bool Mailbox::hasName() const 0150 { 0151 return !mDisplayName.isEmpty(); 0152 } 0153 0154 QString Mailbox::prettyAddress(Quoting quoting) const 0155 { 0156 if (!hasName()) { 0157 return QLatin1StringView(address()); 0158 } 0159 QString s = name(); 0160 if (quoting != QuoteNever) { 0161 addQuotes(s, quoting == QuoteAlways /*bool force*/); 0162 } 0163 0164 if (hasAddress()) { 0165 s += 0166 QLatin1StringView(" <") + QLatin1StringView(address()) + QLatin1Char('>'); 0167 } 0168 return s; 0169 } 0170 0171 void Mailbox::fromUnicodeString(const QString &s) 0172 { 0173 from7BitString(encodeRFC2047Sentence(s, "utf-8")); 0174 } 0175 0176 void Mailbox::from7BitString(const QByteArray &s) 0177 { 0178 const char *cursor = s.constData(); 0179 HeaderParsing::parseMailbox(cursor, cursor + s.length(), *this); 0180 } 0181 0182 QByteArray Mailbox::as7BitString(const QByteArray &encCharset) const 0183 { 0184 if (!hasName()) { 0185 return address(); 0186 } 0187 QByteArray rv; 0188 if (isUsAscii(name())) { 0189 QByteArray tmp = name().toLatin1(); 0190 addQuotes(tmp, false); 0191 rv += tmp; 0192 } else { 0193 rv += encodeRFC2047String(name(), encCharset, true); 0194 } 0195 if (hasAddress()) { 0196 rv += " <" + address() + '>'; 0197 } 0198 return rv; 0199 } 0200 0201 QList<KMime::Types::Mailbox> Mailbox::listFromUnicodeString(const QString &s) { 0202 return listFrom7BitString(encodeRFC2047Sentence(s, "utf-8")); 0203 } 0204 0205 QList<KMime::Types::Mailbox> Mailbox::listFrom7BitString(const QByteArray &s) { 0206 QList<KMime::Types::Mailbox> res; 0207 QList<KMime::Types::Address> maybeAddressList; 0208 const char *scursor = s.constData(); 0209 if (!HeaderParsing::parseAddressList(scursor, scursor + s.size(), maybeAddressList)) { 0210 return res; 0211 } 0212 0213 res.reserve(maybeAddressList.size()); 0214 for (const auto &it : std::as_const(maybeAddressList)) { 0215 res += (it).mailboxList; 0216 } 0217 return res; 0218 } 0219 0220 QString Mailbox::listToUnicodeString(const QList<Mailbox> &mailboxes) { 0221 if (mailboxes.size() == 1) { // QStringList free fast-path for the common case 0222 return mailboxes.at(0).prettyAddress(); 0223 } 0224 0225 QStringList rv; 0226 rv.reserve(mailboxes.count()); 0227 for (const Types::Mailbox &mbox : mailboxes) { 0228 rv.append(mbox.prettyAddress()); 0229 } 0230 return rv.join(QLatin1StringView(", ")); 0231 } 0232 0233 } // namespace Types 0234 0235 } // namespace KMime