File indexing completed on 2024-09-22 04:52:49
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include <typeinfo> 0024 0025 #include <QTextDocument> 0026 #include <QUrl> 0027 #include <QTextCodec> 0028 #include "Message.h" 0029 #include "MailAddress.h" 0030 #include "LowLevelParser.h" 0031 #include "../Model/MailboxTree.h" 0032 #include "../Encoders.h" 0033 #include "../Parser/Rfc5322HeaderParser.h" 0034 0035 namespace Imap 0036 { 0037 namespace Message 0038 { 0039 0040 QList<MailAddress> Envelope::getListOfAddresses(const QVariant &in, const QByteArray &line, const int start) 0041 { 0042 if (in.type() == QVariant::ByteArray) { 0043 if (! in.toByteArray().isNull()) 0044 throw UnexpectedHere("getListOfAddresses: byte array not null", line, start); 0045 } else if (in.type() != QVariant::List) { 0046 throw ParseError("getListOfAddresses: not a list", line, start); 0047 } 0048 0049 QVariantList list = in.toList(); 0050 QList<MailAddress> res; 0051 for (QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) { 0052 if (it->type() != QVariant::List) 0053 throw UnexpectedHere("getListOfAddresses: split item not a list", line, start); // FIXME: wrong offset 0054 res.append(MailAddress(it->toList(), line, start)); 0055 } 0056 return res; 0057 } 0058 0059 Envelope Envelope::fromList(const QVariantList &items, const QByteArray &line, const int start) 0060 { 0061 if (items.size() != 10) 0062 throw ParseError("Envelope::fromList: size != 10", line, start); // FIXME: wrong offset 0063 0064 // date 0065 QDateTime date; 0066 if (items[0].type() == QVariant::ByteArray) { 0067 QByteArray dateStr = items[0].toByteArray(); 0068 if (! dateStr.isEmpty()) { 0069 try { 0070 date = LowLevelParser::parseRFC2822DateTime(dateStr); 0071 } catch (ParseError &) { 0072 // FIXME: log this 0073 //throw ParseError( e.what(), line, start ); 0074 } 0075 } 0076 } 0077 // Otherwise it's "invalid", null. 0078 0079 QString subject = Imap::decodeRFC2047String(items[1].toByteArray()); 0080 0081 QList<MailAddress> from, sender, replyTo, to, cc, bcc; 0082 from = Envelope::getListOfAddresses(items[2], line, start); 0083 sender = Envelope::getListOfAddresses(items[3], line, start); 0084 replyTo = Envelope::getListOfAddresses(items[4], line, start); 0085 to = Envelope::getListOfAddresses(items[5], line, start); 0086 cc = Envelope::getListOfAddresses(items[6], line, start); 0087 bcc = Envelope::getListOfAddresses(items[7], line, start); 0088 0089 LowLevelParser::Rfc5322HeaderParser headerParser; 0090 0091 if (items[8].type() != QVariant::ByteArray) 0092 throw UnexpectedHere("Envelope::fromList: inReplyTo not a QByteArray", line, start); 0093 QByteArray inReplyTo = items[8].toByteArray(); 0094 0095 if (items[9].type() != QVariant::ByteArray) 0096 throw UnexpectedHere("Envelope::fromList: messageId not a QByteArray", line, start); 0097 QByteArray messageId = items[9].toByteArray(); 0098 0099 QByteArray buf; 0100 if (!messageId.isEmpty()) 0101 buf += "Message-Id: " + messageId + "\r\n"; 0102 if (!inReplyTo.isEmpty()) 0103 buf += "In-Reply-To: " + inReplyTo + "\r\n"; 0104 if (!buf.isEmpty()) { 0105 bool ok = headerParser.parse(buf); 0106 if (!ok) { 0107 qDebug() << "Envelope::fromList: malformed headers"; 0108 } 0109 } 0110 // If the Message-Id fails to parse, well, bad luck. This enforced sanitizaion is hopefully better than 0111 // generating garbage in outgoing e-mails. 0112 messageId = headerParser.messageId.size() == 1 ? headerParser.messageId.front() : QByteArray(); 0113 0114 return Envelope(date, subject, from, sender, replyTo, to, cc, bcc, headerParser.inReplyTo, messageId); 0115 } 0116 0117 void Envelope::clear() 0118 { 0119 date = QDateTime(); 0120 subject.clear(); 0121 from.clear(); 0122 sender.clear(); 0123 replyTo.clear(); 0124 to.clear(); 0125 cc.clear(); 0126 bcc.clear(); 0127 inReplyTo.clear(); 0128 messageId.clear(); 0129 } 0130 0131 bool OneMessage::eq(const AbstractData &other) const 0132 { 0133 try { 0134 const OneMessage &o = dynamic_cast<const OneMessage &>(other); 0135 return o.mediaType == mediaType && mediaSubType == o.mediaSubType && 0136 bodyFldParam == o.bodyFldParam && bodyFldId == o.bodyFldId && 0137 bodyFldDesc == o.bodyFldDesc && bodyFldEnc == o.bodyFldEnc && 0138 bodyFldOctets == o.bodyFldOctets && bodyFldMd5 == o.bodyFldMd5 && 0139 bodyFldDsp == o.bodyFldDsp && bodyFldLang == o.bodyFldLang && 0140 bodyFldLoc == o.bodyFldLoc && bodyExtension == o.bodyExtension; 0141 } catch (std::bad_cast &) { 0142 return false; 0143 } 0144 } 0145 0146 bool TextMessage::eq(const AbstractData &other) const 0147 { 0148 try { 0149 const TextMessage &o = dynamic_cast<const TextMessage &>(other); 0150 return OneMessage::eq(o) && bodyFldLines == o.bodyFldLines; 0151 } catch (std::bad_cast &) { 0152 return false; 0153 } 0154 } 0155 0156 QTextStream &TextMessage::dump(QTextStream &s, const int indent) const 0157 { 0158 QByteArray i(indent + 1, ' '); 0159 QByteArray lf("\n"); 0160 0161 return s << QByteArray(indent, ' ') << "TextMessage( " << mediaType << "/" << mediaSubType << lf << 0162 i << "body-fld-param: " << bodyFldParam << lf << 0163 i << "body-fld-id: " << bodyFldId << lf << 0164 i << "body-fld-desc: " << bodyFldDesc << lf << 0165 i << "body-fld-enc: " << bodyFldEnc << lf << 0166 i << "body-fld-octets: " << bodyFldOctets << lf << 0167 i << "bodyFldMd5: " << bodyFldMd5 << lf << 0168 i << "body-fld-dsp: " << bodyFldDsp << lf << 0169 i << "body-fld-lang: " << bodyFldLang << lf << 0170 i << "body-fld-loc: " << bodyFldLoc << lf << 0171 i << "body-extension is " << bodyExtension.typeName() << lf << 0172 i << "body-fld-lines: " << bodyFldLines << lf << 0173 QByteArray(indent, ' ') << ")"; 0174 // FIXME: operator<< for QVariant... 0175 } 0176 0177 bool MsgMessage::eq(const AbstractData &other) const 0178 { 0179 try { 0180 const MsgMessage &o = dynamic_cast<const MsgMessage &>(other); 0181 if (o.body) { 0182 if (body) { 0183 if (*body != *o.body) { 0184 return false; 0185 } 0186 } else { 0187 return false; 0188 } 0189 } else if (body) { 0190 return false; 0191 } 0192 0193 return OneMessage::eq(o) && bodyFldLines == o.bodyFldLines && 0194 envelope == o.envelope; 0195 0196 } catch (std::bad_cast &) { 0197 return false; 0198 } 0199 } 0200 0201 QTextStream &MsgMessage::dump(QTextStream &s, const int indent) const 0202 { 0203 QByteArray i(indent + 1, ' '); 0204 QByteArray lf("\n"); 0205 0206 s << QByteArray(indent, ' ') << "MsgMessage(" << lf; 0207 envelope.dump(s, indent + 1); 0208 s << 0209 i << "body-fld-lines " << bodyFldLines << lf << 0210 i << "body:" << lf; 0211 0212 s << 0213 i << "body-fld-param: " << bodyFldParam << lf << 0214 i << "body-fld-id: " << bodyFldId << lf << 0215 i << "body-fld-desc: " << bodyFldDesc << lf << 0216 i << "body-fld-enc: " << bodyFldEnc << lf << 0217 i << "body-fld-octets: " << bodyFldOctets << lf << 0218 i << "bodyFldMd5: " << bodyFldMd5 << lf << 0219 i << "body-fld-dsp: " << bodyFldDsp << lf << 0220 i << "body-fld-lang: " << bodyFldLang << lf << 0221 i << "body-fld-loc: " << bodyFldLoc << lf << 0222 i << "body-extension is " << bodyExtension.typeName() << lf; 0223 0224 if (body) 0225 body->dump(s, indent + 2); 0226 else 0227 s << i << " (null)"; 0228 return s << lf << QByteArray(indent, ' ') << ")"; 0229 } 0230 0231 QTextStream &BasicMessage::dump(QTextStream &s, const int indent) const 0232 { 0233 QByteArray i(indent + 1, ' '); 0234 QByteArray lf("\n"); 0235 0236 return s << QByteArray(indent, ' ') << "BasicMessage( " << mediaType << "/" << mediaSubType << lf << 0237 i << "body-fld-param: " << bodyFldParam << lf << 0238 i << "body-fld-id: " << bodyFldId << lf << 0239 i << "body-fld-desc: " << bodyFldDesc << lf << 0240 i << "body-fld-enc: " << bodyFldEnc << lf << 0241 i << "body-fld-octets: " << bodyFldOctets << lf << 0242 i << "bodyFldMd5: " << bodyFldMd5 << lf << 0243 i << "body-fld-dsp: " << bodyFldDsp << lf << 0244 i << "body-fld-lang: " << bodyFldLang << lf << 0245 i << "body-fld-loc: " << bodyFldLoc << lf << 0246 i << "body-extension is " << bodyExtension.typeName() << lf << 0247 QByteArray(indent, ' ') << ")"; 0248 // FIXME: operator<< for QVariant... 0249 } 0250 0251 bool MultiMessage::eq(const AbstractData &other) const 0252 { 0253 try { 0254 const MultiMessage &o = dynamic_cast<const MultiMessage &>(other); 0255 0256 if (bodies.count() != o.bodies.count()) { 0257 return false; 0258 } 0259 0260 for (int i = 0; i < bodies.count(); ++i) { 0261 if (bodies[i]) { 0262 if (o.bodies[i]) { 0263 if (*bodies[i] != *o.bodies[i]) { 0264 return false; 0265 } 0266 } else { 0267 return false; 0268 } 0269 } else if (! o.bodies[i]) { 0270 return false; 0271 } 0272 } 0273 0274 return mediaSubType == o.mediaSubType && bodyFldParam == o.bodyFldParam && 0275 bodyFldDsp == o.bodyFldDsp && bodyFldLang == o.bodyFldLang && 0276 bodyFldLoc == o.bodyFldLoc && bodyExtension == o.bodyExtension; 0277 0278 } catch (std::bad_cast &) { 0279 return false; 0280 } 0281 } 0282 0283 QTextStream &MultiMessage::dump(QTextStream &s, const int indent) const 0284 { 0285 QByteArray i(indent + 1, ' '); 0286 QByteArray lf("\n"); 0287 0288 s << QByteArray(indent, ' ') << "MultiMessage( multipart/" << mediaSubType << lf << 0289 i << "body-fld-param " << bodyFldParam << lf << 0290 i << "body-fld-dsp " << bodyFldDsp << lf << 0291 i << "body-fld-lang " << bodyFldLang << lf << 0292 i << "body-fld-loc " << bodyFldLoc << lf << 0293 i << "bodyExtension is " << bodyExtension.typeName() << lf << 0294 i << "bodies: [ " << lf; 0295 0296 for (QList<QSharedPointer<AbstractMessage> >::const_iterator it = bodies.begin(); it != bodies.end(); ++it) 0297 if (*it) { 0298 (**it).dump(s, indent + 2); 0299 s << lf; 0300 } else 0301 s << i << " (null)" << lf; 0302 0303 return s << QByteArray(indent, ' ') << "] )"; 0304 } 0305 0306 AbstractMessage::bodyFldParam_t AbstractMessage::makeBodyFldParam(const QVariant &input, const QByteArray &line, const int start) 0307 { 0308 bodyFldParam_t map; 0309 if (input.type() != QVariant::List) { 0310 if (input.type() == QVariant::ByteArray && input.toByteArray().isNull()) 0311 return map; 0312 throw UnexpectedHere("body-fld-param: not a list / nil", line, start); 0313 } 0314 QVariantList list = input.toList(); 0315 if (list.size() % 2) 0316 throw UnexpectedHere("body-fld-param: wrong number of entries", line, start); 0317 for (int j = 0; j < list.size(); j += 2) 0318 if (list[j].type() != QVariant::ByteArray || list[j+1].type() != QVariant::ByteArray) 0319 throw UnexpectedHere("body-fld-param: string not found", line, start); 0320 else 0321 map[ list[j].toByteArray().toUpper() ] = list[j+1].toByteArray(); 0322 return map; 0323 } 0324 0325 AbstractMessage::bodyFldDsp_t AbstractMessage::makeBodyFldDsp(const QVariant &input, const QByteArray &line, const int start) 0326 { 0327 bodyFldDsp_t res; 0328 0329 if (input.type() != QVariant::List) { 0330 if (input.type() == QVariant::ByteArray) { 0331 if (input.toByteArray().isNull()) { 0332 return res; 0333 } else { 0334 qDebug() << "IMAP Parser warning: body-fld-dsp not a list or nil, got this instead: " << input.toByteArray(); 0335 return res; 0336 } 0337 } 0338 throw UnexpectedHere("body-fld-dsp: not a list / nil", line, start); 0339 } 0340 QVariantList list = input.toList(); 0341 0342 if (list.size() < 1) { 0343 throw ParseError("body-fld-dsp: empty list is not allowed", line, start); 0344 } 0345 if (list[0].type() != QVariant::ByteArray) { 0346 throw UnexpectedHere("body-fld-dsp: first item is not a string", line, start); 0347 } 0348 res.first = list[0].toByteArray(); 0349 if (list.size() > 2) { 0350 throw ParseError("body-fld-dsp: too many items in the list", line, start); 0351 } else if (list.size() == 2) { 0352 res.second = makeBodyFldParam(list[1], line, start); 0353 } else { 0354 qDebug() << "IMAP Parser warning: body-fld-dsp: second item not present, ignoring"; 0355 } 0356 return res; 0357 } 0358 0359 QList<QByteArray> AbstractMessage::makeBodyFldLang(const QVariant &input, const QByteArray &line, const int start) 0360 { 0361 QList<QByteArray> res; 0362 if (input.type() == QVariant::ByteArray) { 0363 if (input.toByteArray().isNull()) // handle NIL 0364 return res; 0365 res << input.toByteArray(); 0366 } else if (input.type() == QVariant::List) { 0367 QVariantList list = input.toList(); 0368 for (QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) 0369 if (it->type() != QVariant::ByteArray) 0370 throw UnexpectedHere("body-fld-lang has wrong structure", line, start); 0371 else 0372 res << it->toByteArray(); 0373 } else 0374 throw UnexpectedHere("body-fld-lang not found", line, start); 0375 return res; 0376 } 0377 0378 uint AbstractMessage::extractUInt(const QVariant &var, const QByteArray &line, const int start) 0379 { 0380 if (var.type() == QVariant::UInt) 0381 return var.toUInt(); 0382 if (var.type() == QVariant::ByteArray) { 0383 bool ok = false; 0384 int number = var.toInt(&ok); 0385 if (ok) { 0386 if (number >= 0) { 0387 return number; 0388 } else { 0389 qDebug() << "Parser warning:" << number << "is not an unsigned int"; 0390 return 0; 0391 } 0392 } else if (var.toByteArray().isEmpty()) { 0393 qDebug() << "Parser warning: expected unsigned int, but got NIL or an empty string instead, yuck"; 0394 return 0; 0395 } else { 0396 throw UnexpectedHere("extractUInt: not a number", line, start); 0397 } 0398 } 0399 throw UnexpectedHere("extractUInt: weird data type", line, start); 0400 } 0401 0402 quint64 AbstractMessage::extractUInt64(const QVariant &var, const QByteArray &line, const int start) 0403 { 0404 if (var.type() == QVariant::ULongLong) 0405 return var.toULongLong(); 0406 if (var.type() == QVariant::ByteArray) { 0407 bool ok = false; 0408 qint64 number = var.toLongLong(&ok); 0409 if (ok) { 0410 if (number >= 0) { 0411 return number; 0412 } else { 0413 qDebug() << "Parser warning:" << number << "is not an unsigned 64 bit int"; 0414 return 0; 0415 } 0416 } else if (var.toByteArray().isEmpty()) { 0417 qDebug() << "Parser warning: expected unsigned 64 bit int, but got NIL or an empty string instead, yuck"; 0418 return 0; 0419 } else { 0420 throw UnexpectedHere("extractUInt64: not a number", line, start); 0421 } 0422 } 0423 throw UnexpectedHere("extractUInt64: weird data type", line, start); 0424 } 0425 0426 0427 QSharedPointer<AbstractMessage> AbstractMessage::fromList(const QVariantList &items, const QByteArray &line, const int start) 0428 { 0429 if (items.size() < 2) 0430 throw NoData("AbstractMessage::fromList: no data", line, start); 0431 0432 if (items[0].type() == QVariant::ByteArray) { 0433 // it's a single-part message, hurray 0434 0435 int i = 0; 0436 QByteArray mediaType = items[i].toByteArray().toLower(); 0437 ++i; 0438 QByteArray mediaSubType = items[i].toByteArray().toLower(); 0439 ++i; 0440 0441 if (items.size() < 7) { 0442 qDebug() << "AbstractMessage::fromList(): body-type-basic(?): yuck, too few items, using what we've got"; 0443 } 0444 0445 bodyFldParam_t bodyFldParam; 0446 if (i < items.size()) { 0447 bodyFldParam = makeBodyFldParam(items[i], line, start); 0448 ++i; 0449 } 0450 0451 QByteArray bodyFldId; 0452 if (i < items.size()) { 0453 if (items[i].type() != QVariant::ByteArray) 0454 throw UnexpectedHere("body-fld-id not recognized as a ByteArray", line, start); 0455 bodyFldId = items[i].toByteArray(); 0456 ++i; 0457 } 0458 0459 QByteArray bodyFldDesc; 0460 if (i < items.size()) { 0461 if (items[i].type() != QVariant::ByteArray) 0462 throw UnexpectedHere("body-fld-desc not recognized as a ByteArray", line, start); 0463 bodyFldDesc = items[i].toByteArray(); 0464 ++i; 0465 } 0466 0467 QByteArray bodyFldEnc; 0468 if (i < items.size()) { 0469 if (items[i].type() != QVariant::ByteArray) 0470 throw UnexpectedHere("body-fld-enc not recognized as a ByteArray", line, start); 0471 bodyFldEnc = items[i].toByteArray(); 0472 ++i; 0473 } 0474 0475 quint64 bodyFldOctets = 0; 0476 if (i < items.size()) { 0477 bodyFldOctets = extractUInt64(items[i], line, start); 0478 ++i; 0479 } 0480 0481 uint bodyFldLines = 0; 0482 Envelope envelope; 0483 QSharedPointer<AbstractMessage> body; 0484 0485 // This is used when the IMAP response fails to parse in a catastrophic manner. It's better than refusing 0486 // to work with that mailbox altogether. In future, we might get a client-side MIME Parser for this :). 0487 // In the meanwhile, it's critical to override the MIME type properly so that the upper layers don't assert 0488 // on, e.g., missing headers (or invalid indexes in there), etc. 0489 #define RETURN_ERROR_BINARY_PART \ 0490 qDebug() << "will return a fake raw part instead of a damaged" << QByteArray(mediaType + '/' + mediaSubType).data() << "part"; \ 0491 bodyFldParam["x-trojita-original-mime-type"] = mediaType + '/' + mediaSubType; \ 0492 return QSharedPointer<AbstractMessage>(new BasicMessage("application", "x-trojita-malformed-part-from-imap-response", \ 0493 bodyFldParam, bodyFldId, bodyFldDesc, bodyFldEnc, bodyFldOctets, \ 0494 QByteArray(), bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant())) 0495 0496 enum { MESSAGE, TEXT, BASIC} kind; 0497 0498 if (mediaType == "message" && mediaSubType == "rfc822") { 0499 // extract envelope, body, body-fld-lines 0500 0501 if (items.size() < 10) 0502 throw NoData("too few fields for a Message-message", line, start); 0503 0504 kind = MESSAGE; 0505 if (items[i].type() == QVariant::ByteArray && items[i].toByteArray().isEmpty()) { 0506 // ENVELOPE is NIL -- a server bug, but there's a chance that perhaps the body might still be readable... 0507 qDebug() << "message/rfc822: yuck, got NIL for envelope"; 0508 } else if (items[i].type() != QVariant::List) { 0509 qDebug() << "message/rfc822: yuck, ENVELOPE is not a list"; 0510 RETURN_ERROR_BINARY_PART; 0511 } else { 0512 envelope = Envelope::fromList(items[i].toList(), line, start); 0513 } 0514 ++i; 0515 0516 if (items[i].type() != QVariant::List) { 0517 // we're screwed, let's fall back to a binary part rendering 0518 qDebug() << "message/rfc822: yuck, got garbage for BODY"; 0519 RETURN_ERROR_BINARY_PART; 0520 } else { 0521 body = AbstractMessage::fromList(items[i].toList(), line, start); 0522 } 0523 ++i; 0524 0525 try { 0526 bodyFldLines = extractUInt(items[i], line, start); 0527 } catch (const UnexpectedHere &) { 0528 qDebug() << "message/rfc822: yuck, invalid body-fld-lines"; 0529 } 0530 ++i; 0531 0532 } else if (mediaType == "text") { 0533 kind = TEXT; 0534 if (i < items.size()) { 0535 // extract body-fld-lines 0536 bodyFldLines = extractUInt(items[i], line, start); 0537 ++i; 0538 } 0539 } else { 0540 // don't extract anything as we're done here 0541 kind = BASIC; 0542 } 0543 0544 // extract body-ext-1part 0545 0546 // body-fld-md5 0547 QByteArray bodyFldMd5; 0548 if (i < items.size()) { 0549 if (items[i].type() != QVariant::ByteArray) 0550 throw UnexpectedHere("body-fld-md5 not a ByteArray", line, start); 0551 bodyFldMd5 = items[i].toByteArray(); 0552 ++i; 0553 } 0554 0555 // body-fld-dsp 0556 bodyFldDsp_t bodyFldDsp; 0557 if (i < items.size()) { 0558 bodyFldDsp = makeBodyFldDsp(items[i], line, start); 0559 ++i; 0560 } 0561 0562 // body-fld-lang 0563 QList<QByteArray> bodyFldLang; 0564 if (i < items.size()) { 0565 bodyFldLang = makeBodyFldLang(items[i], line, start); 0566 ++i; 0567 } 0568 0569 // body-fld-loc 0570 QByteArray bodyFldLoc; 0571 if (i < items.size()) { 0572 if (items[i].type() != QVariant::ByteArray) 0573 throw UnexpectedHere("body-fld-loc not found", line, start); 0574 bodyFldLoc = items[i].toByteArray(); 0575 ++i; 0576 } 0577 0578 // body-extension 0579 QVariant bodyExtension; 0580 if (i < items.size()) { 0581 if (i == items.size() - 1) { 0582 bodyExtension = items[i]; 0583 ++i; 0584 } else { 0585 QVariantList list; 0586 for (; i < items.size(); ++i) 0587 list << items[i]; 0588 bodyExtension = list; 0589 } 0590 } 0591 0592 switch (kind) { 0593 case MESSAGE: 0594 return QSharedPointer<AbstractMessage>( 0595 new MsgMessage(mediaType, mediaSubType, bodyFldParam, 0596 bodyFldId, bodyFldDesc, bodyFldEnc, bodyFldOctets, 0597 bodyFldMd5, bodyFldDsp, bodyFldLang, bodyFldLoc, 0598 bodyExtension, envelope, body, bodyFldLines) 0599 ); 0600 case TEXT: 0601 return QSharedPointer<AbstractMessage>( 0602 new TextMessage(mediaType, mediaSubType, bodyFldParam, 0603 bodyFldId, bodyFldDesc, bodyFldEnc, bodyFldOctets, 0604 bodyFldMd5, bodyFldDsp, bodyFldLang, bodyFldLoc, 0605 bodyExtension, bodyFldLines) 0606 ); 0607 case BASIC: 0608 default: 0609 return QSharedPointer<AbstractMessage>( 0610 new BasicMessage(mediaType, mediaSubType, bodyFldParam, 0611 bodyFldId, bodyFldDesc, bodyFldEnc, bodyFldOctets, 0612 bodyFldMd5, bodyFldDsp, bodyFldLang, bodyFldLoc, 0613 bodyExtension) 0614 ); 0615 } 0616 0617 } else if (items[0].type() == QVariant::List) { 0618 0619 if (items.size() < 2) 0620 throw ParseError("body-type-mpart: structure should be \"body* string\"", line, start); 0621 0622 int i = 0; 0623 0624 QList<QSharedPointer<AbstractMessage> > bodies; 0625 while (items[i].type() == QVariant::List) { 0626 bodies << fromList(items[i].toList(), line, start); 0627 ++i; 0628 } 0629 0630 if (items[i].type() != QVariant::ByteArray) 0631 throw UnexpectedHere("body-type-mpart: media-subtype not recognized", line, start); 0632 QByteArray mediaSubType = items[i].toByteArray().toLower(); 0633 ++i; 0634 0635 // body-ext-mpart 0636 0637 // body-fld-param 0638 bodyFldParam_t bodyFldParam; 0639 if (i < items.size()) { 0640 bodyFldParam = makeBodyFldParam(items[i], line, start); 0641 ++i; 0642 } 0643 0644 // body-fld-dsp 0645 bodyFldDsp_t bodyFldDsp; 0646 if (i < items.size()) { 0647 bodyFldDsp = makeBodyFldDsp(items[i], line, start); 0648 ++i; 0649 } 0650 0651 // body-fld-lang 0652 QList<QByteArray> bodyFldLang; 0653 if (i < items.size()) { 0654 bodyFldLang = makeBodyFldLang(items[i], line, start); 0655 ++i; 0656 } 0657 0658 // body-fld-loc 0659 QByteArray bodyFldLoc; 0660 if (i < items.size()) { 0661 if (items[i].type() != QVariant::ByteArray) 0662 throw UnexpectedHere("body-fld-loc not found", line, start); 0663 bodyFldLoc = items[i].toByteArray(); 0664 ++i; 0665 } 0666 0667 // body-extension 0668 QVariant bodyExtension; 0669 if (i < items.size()) { 0670 if (i == items.size() - 1) { 0671 bodyExtension = items[i]; 0672 ++i; 0673 } else { 0674 QVariantList list; 0675 for (; i < items.size(); ++i) 0676 list << items[i]; 0677 bodyExtension = list; 0678 } 0679 } 0680 0681 return QSharedPointer<AbstractMessage>( 0682 new MultiMessage(bodies, mediaSubType, bodyFldParam, 0683 bodyFldDsp, bodyFldLang, bodyFldLoc, bodyExtension)); 0684 } else { 0685 throw UnexpectedHere("AbstractMessage::fromList: invalid data type of first item", line, start); 0686 } 0687 } 0688 0689 void dumpListOfAddresses(QTextStream &stream, const QList<MailAddress> &list, const int indent) 0690 { 0691 QByteArray lf("\n"); 0692 switch (list.size()) { 0693 case 0: 0694 stream << "[ ]" << lf; 0695 break; 0696 case 1: 0697 stream << "[ " << list.front() << " ]" << lf; 0698 break; 0699 default: { 0700 QByteArray i(indent + 1, ' '); 0701 stream << "[" << lf; 0702 for (QList<MailAddress>::const_iterator it = list.begin(); it != list.end(); ++it) 0703 stream << i << *it << lf; 0704 stream << QByteArray(indent, ' ') << "]" << lf; 0705 } 0706 } 0707 } 0708 0709 QTextStream &Envelope::dump(QTextStream &stream, const int indent) const 0710 { 0711 QByteArray i(indent + 1, ' '); 0712 QByteArray lf("\n"); 0713 stream << QByteArray(indent, ' ') << "Envelope(" << lf << 0714 i << "Date: " << date.toString() << lf << 0715 i << "Subject: " << subject << lf; 0716 stream << i << "From: "; dumpListOfAddresses(stream, from, indent + 1); 0717 stream << i << "Sender: "; dumpListOfAddresses(stream, sender, indent + 1); 0718 stream << i << "Reply-To: "; dumpListOfAddresses(stream, replyTo, indent + 1); 0719 stream << i << "To: "; dumpListOfAddresses(stream, to, indent + 1); 0720 stream << i << "Cc: "; dumpListOfAddresses(stream, cc, indent + 1); 0721 stream << i << "Bcc: "; dumpListOfAddresses(stream, bcc, indent + 1); 0722 stream << 0723 i << "In-Reply-To: " << inReplyTo << lf << 0724 i << "Message-Id: " << messageId << lf; 0725 return stream << QByteArray(indent, ' ') << ")" << lf; 0726 } 0727 0728 QTextStream &operator<<(QTextStream &stream, const Envelope &e) 0729 { 0730 return e.dump(stream, 0); 0731 } 0732 0733 QTextStream &operator<<(QTextStream &stream, const AbstractMessage::bodyFldParam_t &p) 0734 { 0735 stream << "bodyFldParam[ "; 0736 bool first = true; 0737 for (AbstractMessage::bodyFldParam_t::const_iterator it = p.begin(); it != p.end(); ++it, first = false) 0738 stream << (first ? "" : ", ") << it.key() << ": " << it.value(); 0739 return stream << "]"; 0740 } 0741 0742 QTextStream &operator<<(QTextStream &stream, const AbstractMessage::bodyFldDsp_t &p) 0743 { 0744 return stream << "bodyFldDsp( " << p.first << ", " << p.second << ")"; 0745 } 0746 0747 QTextStream &operator<<(QTextStream &stream, const QList<QByteArray> &list) 0748 { 0749 stream << "( "; 0750 bool first = true; 0751 for (QList<QByteArray>::const_iterator it = list.begin(); it != list.end(); ++it, first = false) 0752 stream << (first ? "" : ", ") << *it; 0753 return stream << " )"; 0754 } 0755 0756 bool operator==(const Envelope &a, const Envelope &b) 0757 { 0758 return a.date == b.date && a.subject == b.subject && 0759 a.from == b.from && a.sender == b.sender && a.replyTo == b.replyTo && 0760 a.to == b.to && a.cc == b.cc && a.bcc == b.bcc && 0761 a.inReplyTo == b.inReplyTo && a.messageId == b.messageId; 0762 } 0763 0764 /** @short Extract interesting part-specific metadata from the BODYSTRUCTURE into the actual part 0765 0766 Examples are stuff like the charset, or the suggested filename. 0767 */ 0768 void AbstractMessage::storeInterestingFields(Mailbox::TreeItemPart *p) const 0769 { 0770 p->setBodyFldParam(bodyFldParam); 0771 0772 // Charset 0773 bodyFldParam_t::const_iterator it = bodyFldParam.find("CHARSET"); 0774 if (it != bodyFldParam.end()) { 0775 p->setCharset(*it); 0776 } 0777 0778 // Support for format=flowed, RFC3676 0779 it = bodyFldParam.find("FORMAT"); 0780 if (it != bodyFldParam.end()) { 0781 p->setContentFormat(*it); 0782 0783 it = bodyFldParam.find("DELSP"); 0784 if (it != bodyFldParam.end()) { 0785 p->setContentDelSp(*it); 0786 } 0787 } 0788 0789 // Filename and content-disposition 0790 if (!bodyFldDsp.first.isNull()) { 0791 p->setBodyDisposition(bodyFldDsp.first); 0792 p->setFileName(Imap::extractRfc2231Param(bodyFldDsp.second, "FILENAME")); 0793 } 0794 // Try to look for the obsolete "name" right in the Content-Type header (as parsed by the IMAP server) as a fallback 0795 // As per Thomas' suggestion, an empty-but-specified filename is happily overwritten here by design. 0796 if (p->fileName().isEmpty()) { 0797 p->setFileName(Imap::extractRfc2231Param(bodyFldParam, "NAME")); 0798 } 0799 } 0800 0801 void OneMessage::storeInterestingFields(Mailbox::TreeItemPart *p) const 0802 { 0803 AbstractMessage::storeInterestingFields(p); 0804 p->setTransferEncoding(bodyFldEnc.toLower()); 0805 p->setOctets(bodyFldOctets); 0806 p->setBodyFldId(bodyFldId); 0807 } 0808 0809 void MultiMessage::storeInterestingFields(Mailbox::TreeItemPart *p) const 0810 { 0811 AbstractMessage::storeInterestingFields(p); 0812 0813 // The multipart/related can specify the root part to show 0814 if (mediaSubType == "related") { 0815 bodyFldParam_t::const_iterator it = bodyFldParam.find("START"); 0816 if (it != bodyFldParam.end()) { 0817 p->setMultipartRelatedStartPart(*it); 0818 } 0819 } 0820 } 0821 0822 Mailbox::TreeItemChildrenList TextMessage::createTreeItems(Mailbox::TreeItem *parent) const 0823 { 0824 Mailbox::TreeItemChildrenList list; 0825 Mailbox::TreeItemPart *p = new Mailbox::TreeItemPart(parent, mediaType + "/" + mediaSubType); 0826 storeInterestingFields(p); 0827 list << p; 0828 return list; 0829 } 0830 0831 Mailbox::TreeItemChildrenList BasicMessage::createTreeItems(Mailbox::TreeItem *parent) const 0832 { 0833 Mailbox::TreeItemChildrenList list; 0834 Mailbox::TreeItemPart *p = new Mailbox::TreeItemPart(parent, mediaType + "/" + mediaSubType); 0835 storeInterestingFields(p); 0836 list << p; 0837 return list; 0838 } 0839 0840 Mailbox::TreeItemChildrenList MsgMessage::createTreeItems(Mailbox::TreeItem *parent) const 0841 { 0842 Mailbox::TreeItemChildrenList list; 0843 Mailbox::TreeItemPart *part = new Mailbox::TreeItemPartMultipartMessage(parent, envelope); 0844 part->setChildren(body->createTreeItems(part)); // always returns an empty list -> no need to qDeleteAll() 0845 storeInterestingFields(part); 0846 list << part; 0847 return list; 0848 } 0849 0850 Mailbox::TreeItemChildrenList MultiMessage::createTreeItems(Mailbox::TreeItem *parent) const 0851 { 0852 Mailbox::TreeItemChildrenList list, list2; 0853 Mailbox::TreeItemPart *part = new Mailbox::TreeItemPart(parent, "multipart/" + mediaSubType); 0854 for (QList<QSharedPointer<AbstractMessage> >::const_iterator it = bodies.begin(); it != bodies.end(); ++it) { 0855 list2 << (*it)->createTreeItems(part); 0856 } 0857 part->setChildren(list2); // always returns an empty list -> no need to qDeleteAll() 0858 storeInterestingFields(part); 0859 list << part; 0860 return list; 0861 } 0862 0863 } 0864 } 0865 0866 QDebug operator<<(QDebug dbg, const Imap::Message::Envelope &envelope) 0867 { 0868 using namespace Imap::Message; 0869 return dbg << "Envelope( FROM" << MailAddress::prettyList(envelope.from, MailAddress::FORMAT_READABLE) << 0870 "TO" << MailAddress::prettyList(envelope.to, MailAddress::FORMAT_READABLE) << 0871 "CC" << MailAddress::prettyList(envelope.cc, MailAddress::FORMAT_READABLE) << 0872 "BCC" << MailAddress::prettyList(envelope.bcc, MailAddress::FORMAT_READABLE) << 0873 "SUBJECT" << envelope.subject << 0874 "DATE" << envelope.date << 0875 "MESSAGEID" << envelope.messageId; 0876 } 0877 0878 QDataStream &operator>>(QDataStream &stream, Imap::Message::Envelope &e) 0879 { 0880 return stream >> e.bcc >> e.cc >> e.date >> e.from >> e.inReplyTo >> 0881 e.messageId >> e.replyTo >> e.sender >> e.subject >> e.to; 0882 } 0883 0884 QDataStream &operator<<(QDataStream &stream, const Imap::Message::Envelope &e) 0885 { 0886 return stream << e.bcc << e.cc << e.date << e.from << e.inReplyTo << 0887 e.messageId << e.replyTo << e.sender << e.subject << e.to; 0888 }