File indexing completed on 2024-05-12 05:11:09
0001 /* 0002 SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "akonadi_serializer_mail.h" 0008 #include "akonadi_serializer_mail_debug.h" 0009 #include "messageparts.h" 0010 0011 #include <QDataStream> 0012 #include <QIODevice> 0013 #include <qplugin.h> 0014 0015 #include <KMime/Message> 0016 0017 #include <Akonadi/Item> 0018 #include <akonadi/private/imapparser_p.h> 0019 0020 using namespace Akonadi; 0021 using namespace KMime; 0022 0023 QString StringPool::sharedValue(const QString &value) 0024 { 0025 QMutexLocker lock(&m_mutex); 0026 QSet<QString>::const_iterator it = m_pool.constFind(value); 0027 if (it != m_pool.constEnd()) { 0028 return *it; 0029 } 0030 m_pool.insert(value); 0031 return value; 0032 } 0033 0034 template<typename T> 0035 static void parseAddrList(const QVarLengthArray<QByteArray, 16> &addrList, T *hdr, int version, StringPool &pool) 0036 { 0037 hdr->clear(); 0038 const int count = addrList.count(); 0039 QVarLengthArray<QByteArray, 16> addr; 0040 for (int i = 0; i < count; ++i) { 0041 ImapParser::parseParenthesizedList(addrList[i], addr); 0042 if (addr.count() != 4) { 0043 qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Error parsing envelope address field: " << addrList[i]; 0044 continue; 0045 } 0046 KMime::Types::Mailbox addrField; 0047 if (version == 0) { 0048 addrField.setNameFrom7Bit(addr[0]); 0049 } else if (version == 1) { 0050 addrField.setName(pool.sharedValue(QString::fromUtf8(addr[0]))); 0051 } 0052 KMime::Types::AddrSpec addrSpec; 0053 addrSpec.localPart = pool.sharedValue(QString::fromUtf8(addr[2])); 0054 addrSpec.domain = pool.sharedValue(QString::fromUtf8(addr[3])); 0055 addrField.setAddress(addrSpec); 0056 hdr->addAddress(addrField); 0057 } 0058 } 0059 0060 template<typename T> 0061 static void parseAddrList(QDataStream &stream, T *hdr, int version, StringPool &pool) 0062 { 0063 Q_UNUSED(version) 0064 0065 hdr->clear(); 0066 int count = 0; 0067 stream >> count; 0068 for (int i = 0; i < count; ++i) { 0069 QString str; 0070 KMime::Types::Mailbox mbox; 0071 KMime::Types::AddrSpec addrSpec; 0072 stream >> str; 0073 mbox.setName(pool.sharedValue(str)); 0074 stream >> str; 0075 addrSpec.localPart = pool.sharedValue(str); 0076 stream >> str; 0077 addrSpec.domain = pool.sharedValue(str); 0078 mbox.setAddress(addrSpec); 0079 0080 hdr->addAddress(mbox); 0081 } 0082 } 0083 0084 bool SerializerPluginMail::deserialize(Item &item, const QByteArray &label, QIODevice &data, int version) 0085 { 0086 if (label != MessagePart::Body && label != MessagePart::Envelope && label != MessagePart::Header) { 0087 return false; 0088 } 0089 0090 KMime::Message::Ptr msg; 0091 if (!item.hasPayload()) { 0092 auto m = new Message(); 0093 msg = KMime::Message::Ptr(m); 0094 item.setPayload(msg); 0095 } else { 0096 msg = item.payload<KMime::Message::Ptr>(); 0097 } 0098 0099 if (label == MessagePart::Body) { 0100 QByteArray buffer = data.readAll(); 0101 if (buffer.isEmpty()) { 0102 return true; 0103 } 0104 msg->setContent(buffer); 0105 msg->parse(); 0106 } else if (label == MessagePart::Header) { 0107 QByteArray buffer = data.readAll(); 0108 if (buffer.isEmpty()) { 0109 return true; 0110 } 0111 if (msg->body().isEmpty() && msg->contents().isEmpty()) { 0112 msg->setHead(buffer); 0113 msg->parse(); 0114 } 0115 } else if (label == MessagePart::Envelope) { 0116 if (version <= 1) { 0117 QByteArray buffer = data.readAll(); 0118 if (buffer.isEmpty()) { 0119 return true; 0120 } 0121 QVarLengthArray<QByteArray, 16> env; 0122 ImapParser::parseParenthesizedList(buffer, env); 0123 if (env.count() < 10) { 0124 qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Akonadi KMime Deserializer: Got invalid envelope: " << buffer; 0125 return false; 0126 } 0127 Q_ASSERT(env.count() >= 10); 0128 // date 0129 msg->date()->from7BitString(env[0]); 0130 // subject 0131 msg->subject()->from7BitString(env[1]); 0132 // from 0133 QVarLengthArray<QByteArray, 16> addrList; 0134 ImapParser::parseParenthesizedList(env[2], addrList); 0135 if (!addrList.isEmpty()) { 0136 parseAddrList(addrList, msg->from(), version, m_stringPool); 0137 } 0138 // sender 0139 ImapParser::parseParenthesizedList(env[3], addrList); 0140 if (!addrList.isEmpty()) { 0141 parseAddrList(addrList, msg->sender(), version, m_stringPool); 0142 } 0143 // reply-to 0144 ImapParser::parseParenthesizedList(env[4], addrList); 0145 if (!addrList.isEmpty()) { 0146 parseAddrList(addrList, msg->replyTo(), version, m_stringPool); 0147 } 0148 // to 0149 ImapParser::parseParenthesizedList(env[5], addrList); 0150 if (!addrList.isEmpty()) { 0151 parseAddrList(addrList, msg->to(), version, m_stringPool); 0152 } 0153 // cc 0154 ImapParser::parseParenthesizedList(env[6], addrList); 0155 if (!addrList.isEmpty()) { 0156 parseAddrList(addrList, msg->cc(), version, m_stringPool); 0157 } 0158 // bcc 0159 ImapParser::parseParenthesizedList(env[7], addrList); 0160 if (!addrList.isEmpty()) { 0161 parseAddrList(addrList, msg->bcc(), version, m_stringPool); 0162 } 0163 // in-reply-to 0164 msg->inReplyTo()->from7BitString(env[8]); 0165 // message id 0166 msg->messageID()->from7BitString(env[9]); 0167 // references 0168 if (env.count() > 10) { 0169 msg->references()->from7BitString(env[10]); 0170 } 0171 } else if (version == 2) { 0172 QDataStream stream(&data); 0173 QDateTime dt; 0174 QString str; 0175 0176 stream >> dt; 0177 msg->date()->setDateTime(dt); 0178 stream >> str; 0179 msg->subject()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); 0180 0181 QString inReplyTo; 0182 stream >> inReplyTo; 0183 msg->inReplyTo()->fromUnicodeString(inReplyTo, QByteArrayLiteral("UTF-8")); 0184 stream >> str; 0185 msg->messageID()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); 0186 stream >> str; 0187 if (str == inReplyTo) { 0188 msg->references()->fromIdent(msg->inReplyTo()); 0189 } else { 0190 msg->references()->fromUnicodeString(str, QByteArrayLiteral("UTF-8")); 0191 } 0192 0193 parseAddrList(stream, msg->from(), version, m_stringPool); 0194 parseAddrList(stream, msg->sender(), version, m_stringPool); 0195 parseAddrList(stream, msg->replyTo(), version, m_stringPool); 0196 parseAddrList(stream, msg->to(), version, m_stringPool); 0197 parseAddrList(stream, msg->cc(), version, m_stringPool); 0198 parseAddrList(stream, msg->bcc(), version, m_stringPool); 0199 0200 if (stream.status() == QDataStream::ReadCorruptData || stream.status() == QDataStream::ReadPastEnd) { 0201 qCWarning(AKONADI_SERIALIZER_MAIL_LOG) << "Akonadi KMime Deserializer: Got invalid envelope"; 0202 return false; 0203 } 0204 } 0205 } 0206 0207 return true; 0208 } 0209 0210 template<typename T> 0211 static void serializeAddrList(QDataStream &stream, T *hdr) 0212 { 0213 const KMime::Types::Mailbox::List mb = hdr->mailboxes(); 0214 stream << (qint32)mb.size(); 0215 for (const KMime::Types::Mailbox &mbox : mb) { 0216 stream << mbox.name() << mbox.addrSpec().localPart << mbox.addrSpec().domain; 0217 } 0218 } 0219 0220 void SerializerPluginMail::serialize(const Item &item, const QByteArray &label, QIODevice &data, int &version) 0221 { 0222 version = 1; 0223 0224 auto m = item.payload<KMime::Message::Ptr>(); 0225 if (label == MessagePart::Body) { 0226 data.write(m->encodedContent()); 0227 } else if (label == MessagePart::Envelope) { 0228 version = 2; 0229 QDataStream stream(&data); 0230 stream << m->date()->dateTime() << m->subject()->asUnicodeString() << m->inReplyTo()->asUnicodeString() << m->messageID()->asUnicodeString() 0231 << m->references()->asUnicodeString(); 0232 serializeAddrList(stream, m->from()); 0233 serializeAddrList(stream, m->sender()); 0234 serializeAddrList(stream, m->replyTo()); 0235 serializeAddrList(stream, m->to()); 0236 serializeAddrList(stream, m->cc()); 0237 serializeAddrList(stream, m->bcc()); 0238 } else if (label == MessagePart::Header) { 0239 data.write(m->head()); 0240 } 0241 } 0242 0243 QSet<QByteArray> SerializerPluginMail::parts(const Item &item) const 0244 { 0245 QSet<QByteArray> set; 0246 0247 if (!item.hasPayload<KMime::Message::Ptr>()) { 0248 return set; 0249 } 0250 0251 auto msg = item.payload<KMime::Message::Ptr>(); 0252 if (!msg) { 0253 return set; 0254 } 0255 0256 // FIXME: we really want "has any header" here, but the kmime api doesn't offer that yet 0257 if (msg->hasContent() || msg->hasHeader("Message-ID")) { 0258 set << MessagePart::Envelope << MessagePart::Header; 0259 if (!msg->body().isEmpty() || !msg->contents().isEmpty()) { 0260 set << MessagePart::Body; 0261 } 0262 } 0263 return set; 0264 } 0265 0266 QString SerializerPluginMail::extractGid(const Item &item) const 0267 { 0268 if (!item.hasPayload<KMime::Message::Ptr>()) { 0269 return {}; 0270 } 0271 const auto msg = item.payload<KMime::Message::Ptr>(); 0272 KMime::Headers::MessageID *mid = msg->messageID(false); 0273 if (mid) { 0274 return mid->asUnicodeString(); 0275 } else if (KMime::Headers::Base *uid = msg->headerByType("X-Akonotes-UID")) { 0276 return uid->asUnicodeString(); 0277 } 0278 return {}; 0279 } 0280 0281 #include "moc_akonadi_serializer_mail.cpp"