aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/net/imap/IMAPMessage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/net/imap/IMAPMessage.cpp')
-rw-r--r--vmime-master/src/vmime/net/imap/IMAPMessage.cpp760
1 files changed, 760 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/imap/IMAPMessage.cpp b/vmime-master/src/vmime/net/imap/IMAPMessage.cpp
new file mode 100644
index 0000000..f74a4a4
--- /dev/null
+++ b/vmime-master/src/vmime/net/imap/IMAPMessage.cpp
@@ -0,0 +1,760 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002 Vincent Richard <vincent@vmime.org>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/config.hpp"
+
+
+#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+
+
+#include "vmime/net/imap/IMAPParser.hpp"
+#include "vmime/net/imap/IMAPMessage.hpp"
+#include "vmime/net/imap/IMAPFolder.hpp"
+#include "vmime/net/imap/IMAPFolderStatus.hpp"
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+#include "vmime/net/imap/IMAPMessageStructure.hpp"
+#include "vmime/net/imap/IMAPMessagePart.hpp"
+#include "vmime/net/imap/IMAPMessagePartContentHandler.hpp"
+
+#include "vmime/utility/outputStreamAdapter.hpp"
+
+#include <sstream>
+#include <iterator>
+#include <typeinfo>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+//
+// IMAPMessage_literalHandler
+//
+
+class IMAPMessage_literalHandler : public IMAPParser::literalHandler {
+
+public:
+
+ IMAPMessage_literalHandler(utility::outputStream& os, utility::progressListener* progress) {
+
+ m_target = shared_ptr <target>(new targetStream(progress, os));
+ }
+
+ shared_ptr <target> targetFor(const IMAPParser::component& comp, const int /* data */) {
+
+ if (typeid(comp) == typeid(IMAPParser::msg_att_item)) {
+
+ const int type = static_cast
+ <const IMAPParser::msg_att_item&>(comp).type;
+
+ if (type == IMAPParser::msg_att_item::BODY_SECTION ||
+ type == IMAPParser::msg_att_item::RFC822_TEXT) {
+
+ return m_target;
+ }
+ }
+
+ return shared_ptr <target>();
+ }
+
+ shared_ptr <target> getTarget() {
+
+ return m_target;
+ }
+
+private:
+
+ shared_ptr <target> m_target;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPMessage
+//
+
+
+IMAPMessage::IMAPMessage(
+ const shared_ptr <IMAPFolder>& folder,
+ const size_t num
+)
+ : m_folder(folder),
+ m_num(num),
+ m_size(-1U),
+ m_flags(FLAG_UNDEFINED),
+ m_expunged(false),
+ m_modseq(0),
+ m_structure(null) {
+
+ folder->registerMessage(this);
+}
+
+
+IMAPMessage::IMAPMessage(
+ const shared_ptr <IMAPFolder>& folder,
+ const size_t num,
+ const uid& uid
+)
+ : m_folder(folder),
+ m_num(num),
+ m_size(-1),
+ m_flags(FLAG_UNDEFINED),
+ m_expunged(false),
+ m_uid(uid),
+ m_modseq(0),
+ m_structure(null) {
+
+ folder->registerMessage(this);
+}
+
+
+IMAPMessage::~IMAPMessage() {
+
+ try {
+
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ if (folder) {
+ folder->unregisterMessage(this);
+ }
+
+ } catch (...) {
+
+ // Don't throw in destructor
+ }
+}
+
+
+void IMAPMessage::onFolderClosed() {
+
+ m_folder.reset();
+}
+
+
+size_t IMAPMessage::getNumber() const {
+
+ return m_num;
+}
+
+
+const message::uid IMAPMessage::getUID() const {
+
+ return m_uid;
+}
+
+
+vmime_uint64 IMAPMessage::getModSequence() const {
+
+ return m_modseq;
+}
+
+
+size_t IMAPMessage::getSize() const {
+
+ if (m_size == -1U) {
+ throw exceptions::unfetched_object();
+ }
+
+ return m_size;
+}
+
+
+bool IMAPMessage::isExpunged() const {
+
+ return m_expunged;
+}
+
+
+int IMAPMessage::getFlags() const {
+
+ if (m_flags == FLAG_UNDEFINED) {
+ throw exceptions::unfetched_object();
+ }
+
+ return m_flags;
+}
+
+
+shared_ptr <const messageStructure> IMAPMessage::getStructure() const {
+
+ if (m_structure == NULL) {
+ throw exceptions::unfetched_object();
+ }
+
+ return m_structure;
+}
+
+
+shared_ptr <messageStructure> IMAPMessage::getStructure() {
+
+ if (m_structure == NULL) {
+ throw exceptions::unfetched_object();
+ }
+
+ return m_structure;
+}
+
+
+shared_ptr <const header> IMAPMessage::getHeader() const {
+
+ if (m_header == NULL) {
+ throw exceptions::unfetched_object();
+ }
+
+ return m_header;
+}
+
+
+void IMAPMessage::extract(
+ utility::outputStream& os,
+ utility::progressListener* progress,
+ const size_t start,
+ const size_t length,
+ const bool peek
+) const {
+
+ shared_ptr <const IMAPFolder> folder = m_folder.lock();
+
+ if (!folder) {
+ throw exceptions::folder_not_found();
+ }
+
+ extractImpl(
+ null, os, progress, start, length,
+ EXTRACT_HEADER | EXTRACT_BODY | (peek ? EXTRACT_PEEK : 0)
+ );
+}
+
+
+void IMAPMessage::extractPart(
+ const shared_ptr <const messagePart>& p,
+ utility::outputStream& os,
+ utility::progressListener* progress,
+ const size_t start,
+ const size_t length,
+ const bool peek
+) const {
+
+ shared_ptr <const IMAPFolder> folder = m_folder.lock();
+
+ if (!folder) {
+ throw exceptions::folder_not_found();
+ }
+
+ extractImpl(
+ p, os, progress, start, length,
+ EXTRACT_HEADER | EXTRACT_BODY | (peek ? EXTRACT_PEEK : 0)
+ );
+}
+
+
+void IMAPMessage::fetchPartHeader(const shared_ptr <messagePart>& p) {
+
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ if (!folder) {
+ throw exceptions::folder_not_found();
+ }
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ extractImpl(p, ossAdapter, NULL, 0, -1, EXTRACT_HEADER | EXTRACT_PEEK);
+
+ dynamicCast <IMAPMessagePart>(p)->getOrCreateHeader().parse(oss.str());
+}
+
+
+void IMAPMessage::fetchPartHeaderForStructure(const shared_ptr <messageStructure>& str) {
+
+ for (size_t i = 0, n = str->getPartCount() ; i < n ; ++i) {
+
+ shared_ptr <messagePart> part = str->getPartAt(i);
+
+ // Fetch header of current part
+ fetchPartHeader(part);
+
+ // Fetch header of sub-parts
+ fetchPartHeaderForStructure(part->getStructure());
+ }
+}
+
+
+size_t IMAPMessage::extractImpl(
+ const shared_ptr <const messagePart>& p,
+ utility::outputStream& os,
+ utility::progressListener* progress,
+ const size_t start,
+ const size_t length,
+ const int extractFlags
+) const {
+
+ shared_ptr <const IMAPFolder> folder = m_folder.lock();
+
+ IMAPMessage_literalHandler literalHandler(os, progress);
+
+ if (length == 0) {
+ return 0;
+ }
+
+ // Construct section identifier
+ std::ostringstream section;
+ section.imbue(std::locale::classic());
+
+ if (p != NULL) {
+
+ shared_ptr <const IMAPMessagePart> currentPart = dynamicCast <const IMAPMessagePart>(p);
+ std::vector <size_t> numbers;
+
+ numbers.push_back(currentPart->getNumber());
+ currentPart = currentPart->getParent();
+
+ while (currentPart != NULL) {
+ numbers.push_back(currentPart->getNumber());
+ currentPart = currentPart->getParent();
+ }
+
+ numbers.erase(numbers.end() - 1);
+
+ for (std::vector <size_t>::reverse_iterator it = numbers.rbegin() ; it != numbers.rend() ; ++it) {
+ if (it != numbers.rbegin()) section << ".";
+ section << (*it + 1);
+ }
+ }
+
+ // Build the body descriptor for FETCH
+ /*
+ BODY[] header + body
+ BODY.PEEK[] header + body (peek)
+ BODY[HEADER] header
+ BODY.PEEK[HEADER] header (peek)
+ BODY[TEXT] body
+ BODY.PEEK[TEXT] body (peek)
+ */
+ std::ostringstream bodyDesc;
+ bodyDesc.imbue(std::locale::classic());
+
+ bodyDesc << "BODY";
+
+ if (extractFlags & EXTRACT_PEEK) {
+ bodyDesc << ".PEEK";
+ }
+
+ bodyDesc << "[";
+
+ if (section.str().empty()) {
+
+ // header + body
+ if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) {
+ bodyDesc << "";
+ // body only
+ } else if (extractFlags & EXTRACT_BODY) {
+ bodyDesc << "TEXT";
+ // header only
+ } else if (extractFlags & EXTRACT_HEADER) {
+ bodyDesc << "HEADER";
+ }
+
+ } else {
+
+ bodyDesc << section.str();
+
+ // header + body
+ if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) {
+
+ // First, extract header
+ std::ostringstream header;
+ utility::outputStreamAdapter headerStream(header);
+
+ const size_t headerLength = extractImpl(
+ p, headerStream, /* progress */ NULL,
+ /* start */ 0, /* length */ -1, extractFlags & ~EXTRACT_BODY
+ );
+
+ size_t s = start;
+ size_t l = length;
+
+ if (s < headerLength) {
+
+ if (l == static_cast <size_t>(-1)) {
+
+ os.write(header.str().data() + s, headerLength - s);
+
+ } else {
+
+ size_t headerCopyLength = l;
+
+ if (start + headerCopyLength > headerLength) {
+ headerCopyLength = headerLength - start;
+ }
+
+ os.write(header.str().data() + s, headerCopyLength);
+
+ l -= headerCopyLength;
+ }
+
+ s = 0;
+
+ } else {
+
+ s -= headerLength;
+ }
+
+ // Then, extract body
+ return extractImpl(p, os, progress, s, l, extractFlags & ~EXTRACT_HEADER);
+
+ // header only
+ } else if (extractFlags & EXTRACT_HEADER) {
+
+ bodyDesc << ".MIME"; // "MIME" not "HEADER" for parts
+ }
+ }
+
+ bodyDesc << "]";
+
+ if (start != 0 || length != static_cast <size_t>(-1)) {
+
+ if (length == static_cast <size_t>(-1)) {
+ bodyDesc << "<" << start << "." << static_cast <unsigned int>(-1) << ">";
+ } else {
+ bodyDesc << "<" << start << "." << length << ">";
+ }
+ }
+
+ std::vector <std::string> fetchParams;
+ fetchParams.push_back(bodyDesc.str());
+
+ // Send the request
+ IMAPCommand::FETCH(
+ m_uid.empty() ? messageSet::byNumber(m_num) : messageSet::byUID(m_uid),
+ fetchParams
+ )->send(folder->m_connection);
+
+ // Get the response
+ scoped_ptr <IMAPParser::response> resp(folder->m_connection->readResponse(&literalHandler));
+
+ if (resp->isBad() || resp->response_done->response_tagged->
+ resp_cond_state->status != IMAPParser::resp_cond_state::OK) {
+
+ throw exceptions::command_error("FETCH", resp->getErrorLog(), "bad response");
+ }
+
+
+ if (extractFlags & EXTRACT_BODY) {
+ // TODO: update the flags (eg. flag "\Seen" may have been set)
+ }
+
+ return literalHandler.getTarget()->getBytesWritten();
+}
+
+
+int IMAPMessage::processFetchResponse(
+ const fetchAttributes& options,
+ const IMAPParser::message_data& msgData
+) {
+
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ // Get message attributes
+ int changes = 0;
+
+ for (auto &att : msgData.msg_att->items) {
+
+ switch (att->type) {
+
+ case IMAPParser::msg_att_item::FLAGS: {
+
+ int flags = IMAPUtils::messageFlagsFromFlags(*att->flag_list);
+
+ if (m_flags != flags) {
+ m_flags = flags;
+ changes |= events::messageChangedEvent::TYPE_FLAGS;
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::UID: {
+
+ m_uid = att->uniqueid->value;
+ break;
+ }
+ case IMAPParser::msg_att_item::MODSEQ: {
+
+ m_modseq = att->mod_sequence_value->value;
+ break;
+ }
+ case IMAPParser::msg_att_item::ENVELOPE: {
+
+ if (!options.has(fetchAttributes::FULL_HEADER)) {
+
+ auto* env = att->envelope.get();
+ shared_ptr <vmime::header> hdr = getOrCreateHeader();
+
+ // Date
+ hdr->Date()->setValue(env->env_date->value);
+
+ // Subject
+ text subject;
+ text::decodeAndUnfold(env->env_subject->value, &subject);
+
+ hdr->Subject()->setValue(subject);
+
+ // From
+ mailboxList from;
+ IMAPUtils::convertAddressList(*(env->env_from), from);
+
+ if (!from.isEmpty()) {
+ hdr->From()->setValue(*(from.getMailboxAt(0)));
+ }
+
+ // To
+ mailboxList to;
+ IMAPUtils::convertAddressList(*(env->env_to), to);
+
+ hdr->To()->setValue(to.toAddressList());
+
+ // Sender
+ mailboxList sender;
+ IMAPUtils::convertAddressList(*(env->env_sender), sender);
+
+ if (!sender.isEmpty()) {
+ hdr->Sender()->setValue(*(sender.getMailboxAt(0)));
+ }
+
+ // Reply-to
+ mailboxList replyTo;
+ IMAPUtils::convertAddressList(*(env->env_reply_to), replyTo);
+
+ if (!replyTo.isEmpty()) {
+ hdr->ReplyTo()->setValue(*(replyTo.getMailboxAt(0)));
+ }
+
+ // Cc
+ mailboxList cc;
+ IMAPUtils::convertAddressList(*(env->env_cc), cc);
+
+ if (!cc.isEmpty()) {
+ hdr->Cc()->setValue(cc.toAddressList());
+ }
+
+ // Bcc
+ mailboxList bcc;
+ IMAPUtils::convertAddressList(*(env->env_bcc), bcc);
+
+ if (!bcc.isEmpty()) {
+ hdr->Bcc()->setValue(bcc.toAddressList());
+ }
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_STRUCTURE: {
+
+ m_structure = make_shared <IMAPMessageStructure>(att->body.get());
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_HEADER: {
+
+ getOrCreateHeader()->parse(att->nstring->value);
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_SIZE: {
+
+ m_size = static_cast <size_t>(att->number->value);
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_SECTION: {
+
+ if (!options.has(fetchAttributes::FULL_HEADER)) {
+
+ if (att->section->section_text1 &&
+ att->section->section_text1->type
+ == IMAPParser::section_text::HEADER_FIELDS) {
+
+ header tempHeader;
+ tempHeader.parse(att->nstring->value);
+
+ vmime::header& hdr = *getOrCreateHeader();
+
+ for (auto& fld : tempHeader.getFieldList()) {
+ hdr.appendField(vmime::clone(fld));
+ }
+ }
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::INTERNALDATE:
+ case IMAPParser::msg_att_item::RFC822:
+ case IMAPParser::msg_att_item::RFC822_TEXT:
+ case IMAPParser::msg_att_item::BODY: {
+
+ break;
+ }
+
+ }
+ }
+
+ return changes;
+}
+
+
+shared_ptr <header> IMAPMessage::getOrCreateHeader() {
+
+ if (m_header != NULL) {
+ return m_header;
+ } else {
+ return m_header = make_shared <header>();
+ }
+}
+
+
+void IMAPMessage::setFlags(const int flags, const int mode) {
+
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ if (!folder) {
+ throw exceptions::folder_not_found();
+ }
+
+ if (!m_uid.empty()) {
+ folder->setMessageFlags(messageSet::byUID(m_uid), flags, mode);
+ } else {
+ folder->setMessageFlags(messageSet::byNumber(m_num), flags, mode);
+ }
+}
+
+
+void IMAPMessage::constructParsedMessage(
+ const shared_ptr <bodyPart>& parentPart,
+ const shared_ptr <messageStructure>& str,
+ int level
+) {
+
+ if (level == 0) {
+
+ shared_ptr <messagePart> part = str->getPartAt(0);
+
+ // Copy header
+ shared_ptr <const header> hdr = part->getHeader();
+ parentPart->getHeader()->copyFrom(*hdr);
+
+ // Initialize body
+ parentPart->getBody()->setContents(
+ make_shared <IMAPMessagePartContentHandler>(
+ dynamicCast <IMAPMessage>(shared_from_this()),
+ part, parentPart->getBody()->getEncoding()
+ )
+ );
+
+ constructParsedMessage(parentPart, part->getStructure(), 1);
+
+ } else {
+
+ for (size_t i = 0, n = str->getPartCount() ; i < n ; ++i) {
+
+ shared_ptr <messagePart> part = str->getPartAt(i);
+
+ shared_ptr <bodyPart> childPart = make_shared <bodyPart>();
+
+ // Copy header
+ shared_ptr <const header> hdr = part->getHeader();
+ childPart->getHeader()->copyFrom(*hdr);
+
+ // Initialize body
+ childPart->getBody()->setContents(
+ make_shared <IMAPMessagePartContentHandler>(
+ dynamicCast <IMAPMessage>(shared_from_this()),
+ part, childPart->getBody()->getEncoding()
+ )
+ );
+
+ // Add child part
+ parentPart->getBody()->appendPart(childPart);
+
+ // Construct sub parts
+ constructParsedMessage(childPart, part->getStructure(), ++level);
+ }
+ }
+}
+
+
+shared_ptr <vmime::message> IMAPMessage::getParsedMessage() {
+
+ // Fetch structure
+ shared_ptr <messageStructure> structure;
+
+ try {
+
+ structure = getStructure();
+
+ } catch (exceptions::unfetched_object&) {
+
+ std::vector <shared_ptr <message> > msgs;
+ msgs.push_back(dynamicCast <IMAPMessage>(shared_from_this()));
+
+ m_folder.lock()->fetchMessages(
+ msgs, fetchAttributes(fetchAttributes::STRUCTURE), /* progress */ NULL
+ );
+
+ structure = getStructure();
+ }
+
+ // Fetch header for each part
+ fetchPartHeaderForStructure(structure);
+
+ // Construct message from structure
+ shared_ptr <vmime::message> msg = make_shared <vmime::message>();
+
+ constructParsedMessage(msg, structure);
+
+ return msg;
+}
+
+
+void IMAPMessage::renumber(const size_t number) {
+
+ m_num = number;
+}
+
+
+void IMAPMessage::setExpunged() {
+
+ m_expunged = true;
+}
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+