diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/imap/IMAPMessage.cpp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/imap/IMAPMessage.cpp')
-rw-r--r-- | vmime-master/src/vmime/net/imap/IMAPMessage.cpp | 760 |
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 + |