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 | |
download | smtps-and-pop3s-console-program-aa4d426b4d3527d7e166df1a05058c9a4a0f6683.tar.gz smtps-and-pop3s-console-program-aa4d426b4d3527d7e166df1a05058c9a4a0f6683.zip |
Diffstat (limited to 'vmime-master/src/vmime/net')
157 files changed, 36682 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/builtinServices.inl b/vmime-master/src/vmime/net/builtinServices.inl new file mode 100644 index 0000000..2f67d2e --- /dev/null +++ b/vmime-master/src/vmime/net/builtinServices.inl @@ -0,0 +1,76 @@ +// +// 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 registration helpers +#include "vmime/net/serviceRegistration.inl" + + +#ifndef VMIME_BUILDING_DOC + + +#if VMIME_HAVE_MESSAGING_PROTO_POP3 + #include "vmime/net/pop3/POP3Store.hpp" + REGISTER_SERVICE(pop3::POP3Store, pop3, TYPE_STORE); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/pop3/POP3SStore.hpp" + REGISTER_SERVICE(pop3::POP3SStore, pop3s, TYPE_STORE); + #endif // VMIME_HAVE_TLS_SUPPORT +#endif + + +#if VMIME_HAVE_MESSAGING_PROTO_SMTP + #include "vmime/net/smtp/SMTPTransport.hpp" + REGISTER_SERVICE(smtp::SMTPTransport, smtp, TYPE_TRANSPORT); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/smtp/SMTPSTransport.hpp" + REGISTER_SERVICE(smtp::SMTPSTransport, smtps, TYPE_TRANSPORT); + #endif // VMIME_HAVE_TLS_SUPPORT +#endif + + +#if VMIME_HAVE_MESSAGING_PROTO_IMAP + #include "vmime/net/imap/IMAPStore.hpp" + REGISTER_SERVICE(imap::IMAPStore, imap, TYPE_STORE); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/imap/IMAPSStore.hpp" + REGISTER_SERVICE(imap::IMAPSStore, imaps, TYPE_STORE); + #endif // VMIME_HAVE_TLS_SUPPORT +#endif + + +#if VMIME_HAVE_MESSAGING_PROTO_MAILDIR + #include "vmime/net/maildir/maildirStore.hpp" + REGISTER_SERVICE(maildir::maildirStore, maildir, TYPE_STORE); +#endif + +#if VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + #include "vmime/net/sendmail/sendmailTransport.hpp" + REGISTER_SERVICE(sendmail::sendmailTransport, sendmail, TYPE_TRANSPORT); +#endif + + +#endif // VMIME_BUILDING_DOC diff --git a/vmime-master/src/vmime/net/connectionInfos.hpp b/vmime-master/src/vmime/net/connectionInfos.hpp new file mode 100644 index 0000000..0e519c7 --- /dev/null +++ b/vmime-master/src/vmime/net/connectionInfos.hpp @@ -0,0 +1,68 @@ +// +// 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. +// + +#ifndef VMIME_NET_CONNECTIONINFOS_HPP_INCLUDED +#define VMIME_NET_CONNECTIONINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/object.hpp" + + +namespace vmime { +namespace net { + + +/** Information about the connection used by a service. + */ +class VMIME_EXPORT connectionInfos : public object { + +public: + + /** Return the host to which the service is connected. + * + * @return server host name or address + */ + virtual const string getHost() const = 0; + + /** Return the port to which the service is connected. + * + * @return server port + */ + virtual port_t getPort() const = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_CONNECTIONINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/defaultConnectionInfos.cpp b/vmime-master/src/vmime/net/defaultConnectionInfos.cpp new file mode 100644 index 0000000..889f214 --- /dev/null +++ b/vmime-master/src/vmime/net/defaultConnectionInfos.cpp @@ -0,0 +1,60 @@ +// +// 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 + + +#include "vmime/net/defaultConnectionInfos.hpp" + + +namespace vmime { +namespace net { + + +defaultConnectionInfos::defaultConnectionInfos(const string& host, const port_t port) + : m_host(host), m_port(port) { + +} + + +const string defaultConnectionInfos::getHost() const { + + return m_host; +} + + +port_t defaultConnectionInfos::getPort() const { + + return m_port; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/defaultConnectionInfos.hpp b/vmime-master/src/vmime/net/defaultConnectionInfos.hpp new file mode 100644 index 0000000..f348859 --- /dev/null +++ b/vmime-master/src/vmime/net/defaultConnectionInfos.hpp @@ -0,0 +1,66 @@ +// +// 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. +// + +#ifndef VMIME_NET_DEFAULTCONNECTIONINFOS_HPP_INCLUDED +#define VMIME_NET_DEFAULTCONNECTIONINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/connectionInfos.hpp" + + +namespace vmime { +namespace net { + + +/** Information about the connection used by a service. + */ +class VMIME_EXPORT defaultConnectionInfos : public connectionInfos { + +public: + + defaultConnectionInfos(const string& host, const port_t port); + + const string getHost() const; + port_t getPort() const; + +private: + + string m_host; + port_t m_port; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_DEFAULTCONNECTIONINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/defaultTimeoutHandler.cpp b/vmime-master/src/vmime/net/defaultTimeoutHandler.cpp new file mode 100644 index 0000000..f5dcc7d --- /dev/null +++ b/vmime-master/src/vmime/net/defaultTimeoutHandler.cpp @@ -0,0 +1,78 @@ +// +// 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 + + +#include "vmime/net/defaultTimeoutHandler.hpp" + + +namespace vmime { +namespace net { + + +defaultTimeoutHandler::defaultTimeoutHandler() { + + m_startTime = time(NULL); +} + + +defaultTimeoutHandler::~defaultTimeoutHandler() { + +} + + +bool defaultTimeoutHandler::isTimeOut() { + + return time(NULL) - m_startTime >= 30; +} + + +void defaultTimeoutHandler::resetTimeOut() { + + m_startTime = time(NULL); +} + + +bool defaultTimeoutHandler::handleTimeOut() { + + return false; +} + + + + +shared_ptr <timeoutHandler> defaultTimeoutHandlerFactory::create() { + + return make_shared <defaultTimeoutHandler>(); +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/defaultTimeoutHandler.hpp b/vmime-master/src/vmime/net/defaultTimeoutHandler.hpp new file mode 100644 index 0000000..545c655 --- /dev/null +++ b/vmime-master/src/vmime/net/defaultTimeoutHandler.hpp @@ -0,0 +1,80 @@ +// +// 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. +// + +#ifndef VMIME_NET_DEFAULTTIMEOUTHANDLER_HPP_INCLUDED +#define VMIME_NET_DEFAULTTIMEOUTHANDLER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/timeoutHandler.hpp" + +#include <ctime> + + +namespace vmime { +namespace net { + + +/** A default timeout handler for messaging services. The default action + * is to throw a exceptions::operation_timed_out exception when an + * operation is blocked for more than 30 seconds. + */ +class VMIME_EXPORT defaultTimeoutHandler : public timeoutHandler { + +public: + + defaultTimeoutHandler(); + ~defaultTimeoutHandler(); + + bool isTimeOut(); + void resetTimeOut(); + bool handleTimeOut(); + +private: + + time_t m_startTime; +}; + + +/** A class that creates default timeout handlers. + */ +class defaultTimeoutHandlerFactory : public timeoutHandlerFactory { + +public: + + shared_ptr <timeoutHandler> create(); +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_DEFAULTTIMEOUTHANDLER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/dsnAttributes.cpp b/vmime-master/src/vmime/net/dsnAttributes.cpp new file mode 100644 index 0000000..dc949b9 --- /dev/null +++ b/vmime-master/src/vmime/net/dsnAttributes.cpp @@ -0,0 +1,71 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2020 Jan Osusky <jan@osusky.name> +// +// 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 + + +#include "vmime/net/dsnAttributes.hpp" + + +namespace vmime { +namespace net { + + +dsnAttributes::dsnAttributes(const string& dsnNotify, const string& dsnRet, const string& dsnEnvelopId) + : m_notifications(dsnNotify), m_returnFormat(dsnRet), m_envelopId(dsnEnvelopId) { + +} + + +string dsnAttributes::getNotificationConditions() const { + + return m_notifications; +} + + +string dsnAttributes::getReturnFormat() const { + + return m_returnFormat; +} + + +string dsnAttributes::getEnvelopId() const { + + return m_envelopId; +} + + +bool dsnAttributes::isEmpty() const { + + return m_notifications.empty() && m_returnFormat.empty() && m_envelopId.empty(); +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/dsnAttributes.hpp b/vmime-master/src/vmime/net/dsnAttributes.hpp new file mode 100644 index 0000000..945da28 --- /dev/null +++ b/vmime-master/src/vmime/net/dsnAttributes.hpp @@ -0,0 +1,114 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2020 Jan Osusky <jan@osusky.name> +// +// 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. +// + +#ifndef VMIME_NET_DSNATTRIBUTES_HPP_INCLUDED +#define VMIME_NET_DSNATTRIBUTES_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { + + +/** Holds a set of attributes for Delivery Status Notifications (DSN). + */ +class VMIME_EXPORT dsnAttributes : public object { + +public: + + /** Constructs an empty dsnAttributes object. + */ + dsnAttributes() = default; + + /** Constructs a new dsnAttributes object by copying an existing object. + * + * @param dsn object to copy + */ + dsnAttributes(const dsnAttributes& dsn) = default; + + /** Constructs a new dsnAttributes object by moving an existing object. + * + * @param dsn object (Rvalue reference) to move from. + */ + dsnAttributes(dsnAttributes&& dsn) = default; + + ~dsnAttributes() = default; + + /** Constructs a new dsnAttributes object by specifying the attributes. + * + * @param dsnNotify comma separated list of notification conditions as specified in RFC 1891 + * @param dsnRet content of DSN - full message or headers only ("FULL" or "HDRS") + * @param dsnEnvelopId envelop ID to be able to pair the DSN with original message (plain text not in "<" ">") + */ + dsnAttributes(const string& dsnNotify, const string& dsnRet, const string& dsnEnvelopId); + + /** Returns comma separated list of notification conditions as specified in RFC 1891 + * + * @return comma separated list of notification conditions as specified in RFC 1891 + */ + string getNotificationConditions() const; + + /** Returns requested format of the notification (RET parameter of the ESMTP MAIL command). + * + * @return requested format of the notification. + */ + string getReturnFormat() const; + + /** Returns envelop ID used to pair the DSN with the original message. + * + * @return envelop ID used to pair the DSN with the original message. + */ + string getEnvelopId() const; + + /** Returns whether the object is empty, and no attribute has been set. + * + * @return true if object is empty, or false otherwise + */ + bool isEmpty() const; + +private: + + string m_notifications; + string m_returnFormat; + string m_envelopId; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_NET_DSNATTRIBUTES_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/events.cpp b/vmime-master/src/vmime/net/events.cpp new file mode 100644 index 0000000..8660812 --- /dev/null +++ b/vmime-master/src/vmime/net/events.cpp @@ -0,0 +1,180 @@ +// +// 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 + + +#include "vmime/net/events.hpp" +#include "vmime/net/folder.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { +namespace events { + + +// +// event +// + +event::event() { + +} + + +event::~event() { + +} + + +// +// messageCountEvent +// + +const char* messageCountEvent::EVENT_CLASS = "messageCountEvent"; + + +messageCountEvent::messageCountEvent( + const shared_ptr <folder>& folder, + const Types type, + const std::vector <size_t>& nums +) + : m_folder(folder), + m_type(type) { + + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +shared_ptr <folder> messageCountEvent::getFolder() const { return (m_folder); } +messageCountEvent::Types messageCountEvent::getType() const { return (m_type); } +const std::vector <size_t>& messageCountEvent::getNumbers() const { return (m_nums); } + + +void messageCountEvent::dispatch(messageCountListener* listener) { + + if (m_type == TYPE_ADDED) { + listener->messagesAdded(dynamicCast <messageCountEvent>(shared_from_this())); + } else { + listener->messagesRemoved(dynamicCast <messageCountEvent>(shared_from_this())); + } +} + + +const char* messageCountEvent::getClass() const { + + return EVENT_CLASS; +} + + +// +// messageChangedEvent +// + +const char* messageChangedEvent::EVENT_CLASS = "messageChangedEvent"; + + +messageChangedEvent::messageChangedEvent( + const shared_ptr <folder>& folder, + const Types type, + const std::vector <size_t>& nums +) + : m_folder(folder), + m_type(type) { + + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +shared_ptr <folder> messageChangedEvent::getFolder() const { return (m_folder); } +messageChangedEvent::Types messageChangedEvent::getType() const { return (m_type); } +const std::vector <size_t>& messageChangedEvent::getNumbers() const { return (m_nums); } + + +void messageChangedEvent::dispatch(messageChangedListener* listener) { + + listener->messageChanged(dynamicCast <messageChangedEvent>(shared_from_this())); +} + + +const char* messageChangedEvent::getClass() const { + + return EVENT_CLASS; +} + + +// +// folderEvent +// + +const char* folderEvent::EVENT_CLASS = "folderEvent"; + + +folderEvent::folderEvent( + const shared_ptr <folder>& folder, + const Types type, + const utility::path& oldPath, + const utility::path& newPath +) + : m_folder(folder), + m_type(type), + m_oldPath(oldPath), + m_newPath(newPath) { + +} + + +shared_ptr <folder> folderEvent::getFolder() const { return (m_folder); } +folderEvent::Types folderEvent::getType() const { return (m_type); } + + +void folderEvent::dispatch(folderListener* listener) { + + switch (m_type) { + case TYPE_CREATED: listener->folderCreated(dynamicCast <folderEvent>(shared_from_this())); break; + case TYPE_RENAMED: listener->folderRenamed(dynamicCast <folderEvent>(shared_from_this())); break; + case TYPE_DELETED: listener->folderDeleted(dynamicCast <folderEvent>(shared_from_this())); break; + } +} + + +const char* folderEvent::getClass() const { + + return EVENT_CLASS; +} + + +} // events +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/events.hpp b/vmime-master/src/vmime/net/events.hpp new file mode 100644 index 0000000..2ad64e7 --- /dev/null +++ b/vmime-master/src/vmime/net/events.hpp @@ -0,0 +1,276 @@ +// +// 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. +// + +#ifndef VMIME_NET_EVENTS_HPP_INCLUDED +#define VMIME_NET_EVENTS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/utility/path.hpp" + + +namespace vmime { +namespace net { + +class folder; + +namespace events { + + +/** Event occurring on folders or messages. + */ +class VMIME_EXPORT event : public object, public enable_shared_from_this <event> { + +public: + + event(); + virtual ~event(); + + virtual const char* getClass() const = 0; +}; + + +/** Event about the message count in a folder. + */ +class VMIME_EXPORT messageCountEvent : public event { + +public: + + static const char* EVENT_CLASS; + + + enum Types { + TYPE_ADDED, /**< New messages have been added. */ + TYPE_REMOVED /**< Messages have been expunged (renumbering). */ + }; + + + messageCountEvent( + const shared_ptr <folder>& folder, + const Types type, + const std::vector <size_t>& nums + ); + + /** Return the folder in which messages have been added/removed. + * + * @return folder in which message count changed + */ + shared_ptr <folder> getFolder() const; + + /** Return the event type. + * + * @return event type (see messageCountEvent::Types) + */ + Types getType() const; + + /** Return the numbers of the messages that have been added/removed. + * + * @return a list of message numbers + */ + const std::vector <size_t>& getNumbers() const; + + /** Dispatch the event to the specified listener. + * + * @param listener listener to notify + */ + void dispatch(class messageCountListener* listener); + + + const char* getClass() const; + +private: + + shared_ptr <folder> m_folder; + const Types m_type; + std::vector <size_t> m_nums; +}; + + +/** Listener for events about the message count in a folder. + */ +class VMIME_EXPORT messageCountListener { + +protected: + + virtual ~messageCountListener() { } + +public: + + virtual void messagesAdded(const shared_ptr <messageCountEvent>& event) = 0; + virtual void messagesRemoved(const shared_ptr <messageCountEvent>& event) = 0; +}; + + +/** Event occuring on a message. + */ +class VMIME_EXPORT messageChangedEvent : public event { + +public: + + static const char* EVENT_CLASS; + + + enum Types { + TYPE_FLAGS // flags changed + }; + + + messageChangedEvent( + const shared_ptr <folder>& folder, + const Types type, + const std::vector <size_t>& nums + ); + + /** Return the folder in which messages have changed. + * + * @return folder in which message count changed + */ + shared_ptr <folder> getFolder() const; + + /** Return the event type. + * + * @return event type (see messageChangedEvent::Types) + */ + Types getType() const; + + /** Return the numbers of the messages that have changed. + * + * @return a list of message numbers + */ + const std::vector <size_t>& getNumbers() const; + + /** Dispatch the event to the specified listener. + * + * @param listener listener to notify + */ + void dispatch(class messageChangedListener* listener); + + + const char* getClass() const; + +private: + + shared_ptr <folder> m_folder; + const Types m_type; + std::vector <size_t> m_nums; +}; + + +/** Listener for events occuring on a message. + */ +class VMIME_EXPORT messageChangedListener { + +protected: + + virtual ~messageChangedListener() { } + +public: + + virtual void messageChanged(const shared_ptr <messageChangedEvent>& event) = 0; +}; + + +/** Event occuring on a folder. + */ +class VMIME_EXPORT folderEvent : public event { + +public: + + static const char* EVENT_CLASS; + + + enum Types { + TYPE_CREATED, /**< A folder was created. */ + TYPE_DELETED, /**< A folder was deleted. */ + TYPE_RENAMED /**< A folder was renamed. */ + }; + + + folderEvent( + const shared_ptr <folder>& folder, + const Types type, + const utility::path& oldPath, + const utility::path& newPath + ); + + /** Return the folder on which the event occurred. + * + * @return folder on which the event occurred + */ + shared_ptr <folder> getFolder() const; + + /** Return the event type. + * + * @return event type (see folderEvent::Types) + */ + Types getType() const; + + /** Dispatch the event to the specified listener. + * + * @param listener listener to notify + */ + void dispatch(class folderListener* listener); + + + const char* getClass() const; + +private: + + shared_ptr <folder> m_folder; + const Types m_type; + const utility::path m_oldPath; + const utility::path m_newPath; +}; + + +/** Listener for events occuring on a folder. + */ +class VMIME_EXPORT folderListener { + +protected: + + virtual ~folderListener() { } + +public: + + virtual void folderCreated(const shared_ptr <folderEvent>& event) = 0; + virtual void folderRenamed(const shared_ptr <folderEvent>& event) = 0; + virtual void folderDeleted(const shared_ptr <folderEvent>& event) = 0; +}; + + +} // events +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_EVENTS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/fetchAttributes.cpp b/vmime-master/src/vmime/net/fetchAttributes.cpp new file mode 100644 index 0000000..d9b54b8 --- /dev/null +++ b/vmime-master/src/vmime/net/fetchAttributes.cpp @@ -0,0 +1,98 @@ +// +// 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 + + +#include "vmime/net/fetchAttributes.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { + + +fetchAttributes::fetchAttributes() + : m_predefinedAttribs(0) { + +} + + +fetchAttributes::fetchAttributes(const int attribs) + : m_predefinedAttribs(attribs) { + +} + + +fetchAttributes::fetchAttributes(const fetchAttributes& attribs) + : object() { + + m_predefinedAttribs = attribs.m_predefinedAttribs; + m_headers = attribs.m_headers; +} + + +void fetchAttributes::add(const int attribs) { + + m_predefinedAttribs |= attribs; +} + + +void fetchAttributes::add(const string& header) { + + m_headers.push_back(utility::stringUtils::toLower(header)); +} + + +bool fetchAttributes::has(const int attribs) const { + + return (m_predefinedAttribs & attribs) != 0; +} + + +bool fetchAttributes::has(const string& header) const { + + return std::find( + m_headers.begin(), m_headers.end(), + utility::stringUtils::toLower(header) + ) != m_headers.end(); +} + + +const std::vector <string> fetchAttributes::getHeaderFields() const { + + return m_headers; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/fetchAttributes.hpp b/vmime-master/src/vmime/net/fetchAttributes.hpp new file mode 100644 index 0000000..19d9262 --- /dev/null +++ b/vmime-master/src/vmime/net/fetchAttributes.hpp @@ -0,0 +1,140 @@ +// +// 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. +// + +#ifndef VMIME_NET_FETCHATTRIBUTES_HPP_INCLUDED +#define VMIME_NET_FETCHATTRIBUTES_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { + + +/** Holds a set of attributes to fetch for a message. + */ +class VMIME_EXPORT fetchAttributes : public object { + +public: + + /** Predefined attributes that can be fetched. + */ + enum PredefinedFetchAttributes { + ENVELOPE = (1 << 0), /**< Sender, recipients, date, subject. */ + STRUCTURE = (1 << 1), /**< MIME structure (body parts). */ + CONTENT_INFO = (1 << 2), /**< Top-level content type. */ + FLAGS = (1 << 3), /**< Message flags. */ + SIZE = (1 << 4), /**< Message size (exact or estimated). */ + FULL_HEADER = (1 << 5), /**< Full RFC-[2]822 header. */ + UID = (1 << 6), /**< Unique identifier (protocol specific). */ + IMPORTANCE = (1 << 7), /**< Header fields suitable for use with misc::importanceHelper. */ + + CUSTOM = (1 << 16) /**< Reserved for future use. */ + }; + + /** Constructs an empty fetchAttributes object. + */ + fetchAttributes(); + + /** Constructs a new fetchAttributes object by specifying one or more + * predefined objects. + * + * @param attribs one or more OR-ed values of the PredefinedFetchAttributes enum + */ + fetchAttributes(const int attribs); + + /** Constructs a new fetchAttributes object by copying an existing object. + * + * @param attribs object to copy + */ + fetchAttributes(const fetchAttributes& attribs); + + /** Adds the specified predefined attribute to the set of attributes to fetch. + * + * @param attribs one or more OR-ed values of the PredefinedFetchAttributes enum + */ + void add(const int attribs); + + /** Adds the specified header field to the set of attributes to fetch. + * Fetching custom header fields is not supported by all protocols. + * At this time, only IMAP supports this. + * + * @param header name of header field (eg. "X-Mailer") + */ + void add(const string& header); + + /** Returns true if the set contains the specified attribute(s). + * + * @param attribs one or more OR-ed values of the PredefinedFetchAttributes enum + * @return true if the specified attributes are to be fetched + */ + bool has(const int attribs) const; + + /** Returns true if the set contains the specified header field. + * + * @param header name of header field (eg. "X-Mailer") + * @return true if the specified header fields are to be fetched + */ + bool has(const string& header) const; + + /** Returns true if the set contains the specified attribute(s). + * + * \deprecated Use the has() methods instead + * + * @param attribs one or more OR-ed values of the PredefinedFetchAttributes enum + * @return true if the specified attributes are to be fetched + */ + VMIME_DEPRECATED inline bool operator&(const int attribs) const { + return has(attribs); + } + + /** Returns a list of header fields to fetch. + * + * @return list of header names (eg. "X-Mailer") + */ + const std::vector <string> getHeaderFields() const; + +private: + + int m_predefinedAttribs; + std::vector <string> m_headers; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_NET_FETCHATTRIBUTES_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/folder.cpp b/vmime-master/src/vmime/net/folder.cpp new file mode 100644 index 0000000..6e01b60 --- /dev/null +++ b/vmime-master/src/vmime/net/folder.cpp @@ -0,0 +1,134 @@ +// +// 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 + + +#include "vmime/net/folder.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { + + +int folder::getType() { + + return getAttributes().getType(); +} + + +int folder::getFlags() { + + return getAttributes().getFlags(); +} + + +void folder::addMessageChangedListener(events::messageChangedListener* l) { + + m_messageChangedListeners.push_back(l); +} + + +void folder::removeMessageChangedListener(events::messageChangedListener* l) { + + std::remove(m_messageChangedListeners.begin(), m_messageChangedListeners.end(), l); +} + + +void folder::notifyMessageChanged(const shared_ptr <events::messageChangedEvent>& event) { + + for (std::list <events::messageChangedListener*>::iterator + it = m_messageChangedListeners.begin() ; it != m_messageChangedListeners.end() ; ++it) { + + event->dispatch(*it); + } +} + + +void folder::addMessageCountListener(events::messageCountListener* l) { + + m_messageCountListeners.push_back(l); +} + + +void folder::removeMessageCountListener(events::messageCountListener* l) { + + std::remove(m_messageCountListeners.begin(), m_messageCountListeners.end(), l); +} + + +void folder::notifyMessageCount(const shared_ptr <events::messageCountEvent>& event) { + + for (std::list <events::messageCountListener*>::iterator + it = m_messageCountListeners.begin() ; it != m_messageCountListeners.end() ; ++it) { + + event->dispatch(*it); + } +} + + +void folder::addFolderListener(events::folderListener* l) { + + m_folderListeners.push_back(l); +} + + +void folder::removeFolderListener(events::folderListener* l) { + + std::remove(m_folderListeners.begin(), m_folderListeners.end(), l); +} + + +void folder::notifyFolder(const shared_ptr <events::folderEvent>& event) { + + for (std::list <events::folderListener*>::iterator + it = m_folderListeners.begin() ; it != m_folderListeners.end() ; ++it) { + + event->dispatch(*it); + } +} + + +void folder::notifyEvent(const shared_ptr <events::event>& event) { + + if (event->getClass() == events::messageCountEvent::EVENT_CLASS) { + notifyMessageCount(dynamicCast <events::messageCountEvent>(event)); + } else if (event->getClass() == events::messageChangedEvent::EVENT_CLASS) { + notifyMessageChanged(dynamicCast <events::messageChangedEvent>(event)); + } else if (event->getClass() == events::folderEvent::EVENT_CLASS) { + notifyFolder(dynamicCast <events::folderEvent>(event)); + } +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/folder.hpp b/vmime-master/src/vmime/net/folder.hpp new file mode 100644 index 0000000..4d5cf6e --- /dev/null +++ b/vmime-master/src/vmime/net/folder.hpp @@ -0,0 +1,440 @@ +// +// 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. +// + +#ifndef VMIME_NET_FOLDER_HPP_INCLUDED +#define VMIME_NET_FOLDER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" +#include "vmime/dateTime.hpp" + +#include "vmime/message.hpp" +#include "vmime/net/message.hpp" +#include "vmime/net/messageSet.hpp" +#include "vmime/net/events.hpp" +#include "vmime/net/folderStatus.hpp" +#include "vmime/net/fetchAttributes.hpp" +#include "vmime/net/folderAttributes.hpp" + +#include "vmime/utility/path.hpp" +#include "vmime/utility/stream.hpp" +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace net { + + +class store; + + +/** Abstract representation of a folder in a message store. + */ +class VMIME_EXPORT folder : public object, public enable_shared_from_this <folder> { + +protected: + + folder(const folder&) : object(), enable_shared_from_this <folder>() { } + folder() { } + + + enum PrivateConstants { + TYPE_UNDEFINED = 9999, /**< Used internally to indicate type has not + been initialized yet. */ + FLAG_UNDEFINED = 9999 /**< Used internally to indicate flags have not + been initialized yet. */ + }; + +public: + + virtual ~folder() { } + + /** Type used for fully qualified path name of a folder. + */ + typedef vmime::utility::path path; + + + /** Open mode. + */ + enum Modes { + MODE_READ_ONLY, /**< Read-only mode (no modification to folder or messages is possible). */ + MODE_READ_WRITE /**< Full access mode (read and write). */ + }; + + + /** Return the type of this folder. + * + * \deprecated Use the getAttributes().getType() method instead + * + * @return folder type (see folderAttributes::Types enum) + */ + int getType(); + + /** Return the flags of this folder. + * + * \deprecated Use the getAttributes().getFlags() method instead + * + * @return folder flags (see folderAttributes::Flags enum) + */ + int getFlags(); + + /** Return the attributes of the folder. + * + * @return folder attributes (see folder::Flags) + */ + virtual const folderAttributes getAttributes() = 0; + + /** Return the mode in which the folder has been open. + * + * @return folder opening mode (see folder::Modes) + */ + virtual int getMode() const = 0; + + /** Return the name of this folder. + * + * @return folder name + */ + virtual const folder::path::component getName() const = 0; + + /** Return the fully qualified path name of this folder. + * + * @return absolute path of the folder + */ + virtual const folder::path getFullPath() const = 0; + + /** Open this folder. + * + * @param mode open mode (see folder::Modes) + * @param failIfModeIsNotAvailable if set to false and if the requested mode + * is not available, a more restricted mode will be selected automatically. + * If set to true and if the requested mode is not available, the opening + * will fail. + * @throw exceptions::net_exception if an error occurs + * @throw exceptions::folder_already_open if the folder is already open + * in the same session + */ + virtual void open(const int mode, bool failIfModeIsNotAvailable = false) = 0; + + /** Close this folder. + * + * @param expunge if set to true, deleted messages are expunged + * @throw exceptions::net_exception if an error occurs + */ + virtual void close(const bool expunge) = 0; + + /** Create this folder. + * + * @param attribs attributes of the new folder + * @throw exceptions::net_exception if an error occurs + */ + virtual void create(const folderAttributes& attribs) = 0; + + /** Test whether this folder exists. + * + * @return true if the folder exists, false otherwise + */ + virtual bool exists() = 0; + + /** Delete this folder. + * The folder should be closed before attempting to delete it. + * + * @throw exceptions::net_exception if an error occurs + */ + virtual void destroy() = 0; + + /** Test whether this folder is open. + * + * @return true if the folder is open, false otherwise + */ + virtual bool isOpen() const = 0; + + /** Get a new reference to a message in this folder, given its number. + * + * @param num message sequence number + * @return a new object referencing the specified message + * @throw exceptions::net_exception if an error occurs + */ + virtual shared_ptr <message> getMessage(const size_t num) = 0; + + /** Get new references to messages in this folder, given either their + * sequence numbers or UIDs. + * + * To retrieve messages by their number, use: + * \code{.cpp} + * // Get messages from sequence number 5 to sequence number 8 (including) + * folder->getMessage(vmime::net::messageSet::byNumber(5, 8)); + * + * // Get all messages in the folder, starting from number 42 + * folder->getMessage(vmime::net::messageSet::byNumber(42, -1)); + * \endcode + * Or, to retrieve messages by their UID, use: + * \code{.cpp} + * // Get messages from UID 1000 to UID 1042 (including) + * folder->getMessage(vmime::net::messageSet::byUID(1000, 1042)); + * + * // Get message with UID 1042 + * folder->getMessage(vmime::net::messageSet::byUID(1042)); + * + * // Get all messages in the folder, starting from UID 1000 + * folder->getMessage(vmime::net::messageSet::byUID(1000, "*")); + * \endcode + * + * @param msgs index set of messages to retrieve + * @return new objects referencing the specified messages + * @throw exceptions::net_exception if an error occurs + */ + virtual std::vector <shared_ptr <message> > getMessages(const messageSet& msgs) = 0; + + /** Return the number of messages in this folder. + * + * @return number of messages in the folder + */ + virtual size_t getMessageCount() = 0; + + /** Get a new reference to a sub-folder in this folder. + * + * @param name sub-folder name + * @return a new object referencing the specified folder + * @throw exceptions::net_exception if an error occurs + */ + virtual shared_ptr <folder> getFolder(const folder::path::component& name) = 0; + + /** Get the list of all sub-folders in this folder. + * + * @param recursive if set to true, all the descendant are returned. + * If set to false, only the direct children are returned. + * @return list of sub-folders + * @throw exceptions::net_exception if an error occurs + */ + virtual std::vector <shared_ptr <folder> > getFolders(const bool recursive = false) = 0; + + /** Rename (move) this folder to another location. + * + * @param newPath new path of the folder + * @throw exceptions::net_exception if an error occurs + */ + virtual void rename(const folder::path& newPath) = 0; + + /** Remove one or more messages from this folder. + * + * @param msgs index set of messages to delete + * @throw exceptions::net_exception if an error occurs + */ + virtual void deleteMessages(const messageSet& msgs) = 0; + + /** Change the flags for one or more messages in this folder. + * + * @param msgs index set of messages on which to set the flags + * @param flags set of flags (see message::Flags) + * @param mode indicate how to treat old and new flags (see message::FlagsModes) + * @throw exceptions::net_exception if an error occurs + */ + virtual void setMessageFlags(const messageSet& msgs, const int flags, const int mode = message::FLAG_MODE_SET) = 0; + + /** Add a message to this folder. + * + * @param msg message to add (data: header + body) + * @param flags flags for the new message (if -1, default flags are used) + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progress listener, or NULL if not used + * @return a message set containing the number or UID of the new message, or + * an empty set if the information could not be obtained (ie. the server does not + * support returning the number or UID of an added message) + * @throw exceptions::net_exception if an error occurs + */ + virtual messageSet addMessage( + const shared_ptr <vmime::message>& msg, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ) = 0; + + /** Add a message to this folder. + * + * @param is message to add (data: header + body) + * @param size size of the message to add (in bytes) + * @param flags flags for the new message (if -1, default flags are used) + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progress listener, or NULL if not used + * @return a message set containing the number or UID of the new message, or + * an empty set if the information could not be obtained (ie. the server does not + * support returning the number or UID of an added message) + * @throw exceptions::net_exception if an error occurs + */ + virtual messageSet addMessage( + utility::inputStream& is, + const size_t size, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ) = 0; + + /** Copy messages from this folder to another folder. + * + * @param dest destination folder path + * @param msgs index set of messages to copy + * @return a message set containing the number(s) or UID(s) of the copied message(s), + * or an empty set if the information could not be obtained (ie. the server does not + * support returning the number or UID of a copied message) + * @throw exceptions::net_exception if an error occurs + */ + virtual messageSet copyMessages( + const folder::path& dest, + const messageSet& msgs + ) = 0; + + /** Request folder status without opening it. + * + * \deprecated Use the new getStatus() method + * + * @param count will receive the number of messages in the folder + * @param unseen will receive the number of unseen messages in the folder + * @throw exceptions::net_exception if an error occurs + */ + virtual void status(size_t& count, size_t& unseen) = 0; + + /** Request folder status without opening it. + * + * @return current folder status + * @throw exceptions::net_exception if an error occurs + */ + virtual shared_ptr <folderStatus> getStatus() = 0; + + /** Expunge deleted messages. + * + * @throw exceptions::net_exception if an error occurs + */ + virtual void expunge() = 0; + + /** Return a new folder object referencing the parent folder of this folder. + * + * @return parent folder object + */ + virtual shared_ptr <folder> getParent() = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual shared_ptr <const store> getStore() const = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual shared_ptr <store> getStore() = 0; + + /** Fetch objects for the specified messages. + * + * @param msg list of message sequence numbers + * @param attribs set of attributes to fetch + * @param progress progress listener, or NULL if not used + * @throw exceptions::net_exception if an error occurs + */ + virtual void fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& attribs, + utility::progressListener* progress = NULL + ) = 0; + + /** Fetch objects for the specified message. + * + * @param msg the message + * @param attribs set of attributes to fetch + * @throw exceptions::net_exception if an error occurs + */ + virtual void fetchMessage( + const shared_ptr <message>& msg, + const fetchAttributes& attribs + ) = 0; + + /** Get new references to messages in this folder, given either their + * sequence numbers or UIDs, and fetch objects for them at the same time. + * + * @param msgs index set of messages to retrieve + * @param attribs set of attributes to fetch + * @return new objects referencing the specified messages + * @throw exceptions::net_exception if an error occurs + * @see folder::getMessages() + * @see folder::fetchMessages() + */ + virtual std::vector <shared_ptr <message> > getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs + ) = 0; + + /** Return the list of fetchable objects supported by + * the underlying protocol (see folder::fetchAttributes). + * + * @return list of supported fetchable objects + */ + virtual int getFetchCapabilities() const = 0; + + /** Return the sequence numbers of messages whose UID equal or greater than + * the specified UID. + * + * @param uid the uid of the first message + * @throw exceptions::net_exception if an error occurs + */ + virtual std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid) = 0; + + // Event listeners + void addMessageChangedListener(events::messageChangedListener* l); + void removeMessageChangedListener(events::messageChangedListener* l); + + void addMessageCountListener(events::messageCountListener* l); + void removeMessageCountListener(events::messageCountListener* l); + + void addFolderListener(events::folderListener* l); + void removeFolderListener(events::folderListener* l); + +protected: + + void notifyMessageChanged(const shared_ptr <events::messageChangedEvent>& event); + void notifyMessageCount(const shared_ptr <events::messageCountEvent>& event); + void notifyFolder(const shared_ptr <events::folderEvent>& event); + void notifyEvent(const shared_ptr <events::event>& event); + +private: + + std::list <events::messageChangedListener*> m_messageChangedListeners; + std::list <events::messageCountListener*> m_messageCountListeners; + std::list <events::folderListener*> m_folderListeners; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_FOLDER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/folderAttributes.cpp b/vmime-master/src/vmime/net/folderAttributes.cpp new file mode 100644 index 0000000..107007c --- /dev/null +++ b/vmime-master/src/vmime/net/folderAttributes.cpp @@ -0,0 +1,122 @@ +// +// 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 + + +#include "vmime/net/folderAttributes.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { + + +folderAttributes::folderAttributes() + : m_type(TYPE_CONTAINS_FOLDERS | TYPE_CONTAINS_MESSAGES), + m_flags(0), + m_specialUse(SPECIALUSE_NONE) { + +} + + +folderAttributes::folderAttributes(const folderAttributes& attribs) + : object(), + m_type(attribs.m_type), + m_flags(attribs.m_flags), + m_userFlags(attribs.m_userFlags), + m_specialUse(attribs.m_specialUse) { + +} + + +int folderAttributes::getType() const { + + return m_type; +} + + +void folderAttributes::setType(const int type) { + + m_type = type; +} + + +int folderAttributes::getSpecialUse() const { + + return m_specialUse; +} + + +void folderAttributes::setSpecialUse(const int use) { + + m_specialUse = use; +} + + +int folderAttributes::getFlags() const { + + return m_flags; +} + + +void folderAttributes::setFlags(const int flags) { + + m_flags = flags; +} + + +bool folderAttributes::hasFlag(const int flag) { + + return (m_flags & flag) != 0; +} + + +const std::vector <string> folderAttributes::getUserFlags() const { + + return m_userFlags; +} + + +void folderAttributes::setUserFlags(const std::vector <string>& flags) { + + m_userFlags = flags; +} + + +bool folderAttributes::hasUserFlag(const string& flag) { + + return std::find(m_userFlags.begin(), m_userFlags.end(), flag) != m_userFlags.end(); +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/folderAttributes.hpp b/vmime-master/src/vmime/net/folderAttributes.hpp new file mode 100644 index 0000000..715b72a --- /dev/null +++ b/vmime-master/src/vmime/net/folderAttributes.hpp @@ -0,0 +1,177 @@ +// +// 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. +// + +#ifndef VMIME_NET_FOLDERATTRIBUTES_HPP_INCLUDED +#define VMIME_NET_FOLDERATTRIBUTES_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { + + +/** Holds a set of attributes for a folder. + */ +class VMIME_EXPORT folderAttributes : public object { + +public: + + /** Folder types. + */ + enum Types { + TYPE_CONTAINS_FOLDERS = (1 << 0), /**< Folder can contain folders. */ + TYPE_CONTAINS_MESSAGES = (1 << 1) /**< Folder can contain messages. */ + }; + + /** Folder flags. + */ + enum Flags { + FLAG_HAS_CHILDREN = (1 << 0), /**< Folder contains subfolders. */ + FLAG_NO_OPEN = (1 << 1) /**< Folder cannot be open. */ + }; + + /** Folder special uses. + * Not all protocols support this. At the current time, only IMAP supports this, + * if the server has the SPECIAL-USE capability. + */ + enum SpecialUses { + SPECIALUSE_NONE, /**< User folder, no special use (or unknown). */ + SPECIALUSE_ALL, /**< Virtual folder containing all messages. */ + SPECIALUSE_INBOX, /**< Inbox. */ + SPECIALUSE_ARCHIVE, /**< Folder is used to archives messages (server-dependent). */ + SPECIALUSE_DRAFTS, /**< Folder is used to hold draft messages - typically, messages + that are being composed but have not yet been sent. */ + SPECIALUSE_FLAGGED, /**< Virtual folder containing all messages which are marked + in some way as "important" or "flagged". */ + SPECIALUSE_JUNK, /**< Folder is used to hold junk mail. */ + SPECIALUSE_SENT, /**< Folder is is used to hold copies of messages that have + been sent. */ + SPECIALUSE_TRASH, /**< Folder is used to hold messages that have been deleted or + marked for deletion (may be a virtual folder). */ + SPECIALUSE_IMPORTANT /**< Folder contains messages that are likely important to the + user. */ + }; + + + /** Construct a new folderAttributes object with the default set of attributes. + */ + folderAttributes(); + + /** Construct a new folderAttributes object by copying an existing object. + * + * @param attribs object to copy + */ + folderAttributes(const folderAttributes& attribs); + + /** Return the type of the folder. + * + * @return combination of one ore more folder types (see folderAttributes::Types enum) + */ + int getType() const; + + /** Set the type of the folder. + * + * @param type combination of one ore more folder types (see folderAttributes::Types enum) + */ + void setType(const int type); + + /** Return the special use of the folder. + * Not all protocols support this. At the current time, only IMAP supports this, + * if the server has the SPECIAL-USE capability. + * + * @return a value which indicates a special use (see folderAttributes::SpecialUses enum) + */ + int getSpecialUse() const; + + /** Set the special use of the folder. + * Not all protocols support this. At the current time, only IMAP supports this, + * if the server has the SPECIAL-USE capability. + * + * @param use a value which indicates a special use (see folderAttributes::SpecialUses enum) + */ + void setSpecialUse(const int use); + + /** Return the standard (non-user) flags of the folder. + * + * @return combination of one ore more folder flags (see folderAttributes::Flags enum) + */ + int getFlags() const; + + /** Set the standard (non-user) flags of the folder. + * + * @param type combination of one ore more folder flags (see folderAttributes::Flags enum) + */ + void setFlags(const int flags); + + /** Return whether the specified folder flag(s) is/are set. + * + * @param flag combination of one ore more folder flags (see folderAttributes::Flags enum) + * @return true if the specified flags are all set, or false otherwise + */ + bool hasFlag(const int flag); + + /** Set the user-defined flags of the folder. + * + * @return a list of user-defined flags + */ + const std::vector <string> getUserFlags() const; + + /** Set the user-defined flags of the folder. + * + * @param flags a list of user-defined flags + */ + void setUserFlags(const std::vector <string>& flags); + + /** Return whether the specified user-defined flag is set. + * + * @return true if the specified flag is set, or false otherwise + */ + bool hasUserFlag(const string& flag); + +private: + + int m_type; + int m_flags; + std::vector <string> m_userFlags; + int m_specialUse; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_NET_FOLDERATTRIBUTES_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/folderStatus.hpp b/vmime-master/src/vmime/net/folderStatus.hpp new file mode 100644 index 0000000..fcc4afc --- /dev/null +++ b/vmime-master/src/vmime/net/folderStatus.hpp @@ -0,0 +1,73 @@ +// +// 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. +// + +#ifndef VMIME_NET_FOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_FOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/base.hpp" + + +namespace vmime { +namespace net { + + +/** Holds the status of a mail store folder. + */ +class VMIME_EXPORT folderStatus : public object { + +public: + + /** Returns the total number of messages in the folder. + * + * @return number of messages + */ + virtual size_t getMessageCount() const = 0; + + /** Returns the number of unseen messages in the folder. + * + * @return number of unseen messages + */ + virtual size_t getUnseenCount() const = 0; + + /** Clones this object. + * + * @return a copy of this object + */ + virtual shared_ptr <folderStatus> clone() const = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_FOLDERSTATUS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPCommand.cpp b/vmime-master/src/vmime/net/imap/IMAPCommand.cpp new file mode 100644 index 0000000..8911ed0 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPCommand.cpp @@ -0,0 +1,437 @@ +// +// 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/IMAPCommand.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPUtils.hpp" + +#include <sstream> + + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPCommand::IMAPCommand(const string& text, const string& traceText) + : m_text(text), + m_traceText(traceText) { + +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::LOGIN(const string& username, const string& password) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "LOGIN " << IMAPUtils::quoteString(username) + << " " << IMAPUtils::quoteString(password); + + std::ostringstream trace; + trace.imbue(std::locale::classic()); + trace << "LOGIN {username} {password}"; + + return createCommand(cmd.str(), trace.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::AUTHENTICATE(const string& mechName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTHENTICATE " << mechName; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::AUTHENTICATE(const string& mechName, const string& initialResponse) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTHENTICATE " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::LIST(const string& refName, const string& mailboxName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "LIST " << IMAPUtils::quoteString(refName) + << " " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::SELECT( + const bool readOnly, + const string& mailboxName, + const std::vector <string>& params +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (readOnly) { + cmd << "EXAMINE "; + } else { + cmd << "SELECT "; + } + + cmd << IMAPUtils::quoteString(mailboxName); + + if (!params.empty()) { + + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::STATUS( + const string& mailboxName, + const std::vector <string>& attribs +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "STATUS " << IMAPUtils::quoteString(mailboxName); + + cmd << " ("; + + for (size_t i = 0, n = attribs.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << attribs[i]; + } + + cmd << ")"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::CREATE( + const string& mailboxName, + const std::vector <string>& params +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "CREATE " << IMAPUtils::quoteString(mailboxName); + + if (!params.empty()) { + + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::DELETE(const string& mailboxName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "DELETE " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::RENAME( + const string& mailboxName, + const string& newMailboxName +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RENAME " << IMAPUtils::quoteString(mailboxName) + << " " << IMAPUtils::quoteString(newMailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::FETCH( + const messageSet& msgs, + const std::vector <string>& params +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) { + cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs); + } else { + cmd << "FETCH " << IMAPUtils::messageSetToSequenceSet(msgs); + } + + if (params.size() == 1) { + + cmd << " " << params[0]; + + } else { + + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::STORE( + const messageSet& msgs, + const int mode, + const std::vector <string>& flags +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) { + cmd << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs); + } else { + cmd << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs); + } + + if (mode == message::FLAG_MODE_ADD) { + cmd << " +FLAGS "; + } else if (mode == message::FLAG_MODE_REMOVE) { + cmd << " -FLAGS "; + } else { // if (mode == message::FLAG_MODE_SET) + cmd << " FLAGS "; + } + + cmd << "("; + + for (size_t i = 0, n = flags.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << flags[i]; + } + + cmd << ")"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::APPEND( + const string& mailboxName, + const std::vector <string>& flags, + vmime::datetime* date, + const size_t size +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "APPEND " << IMAPUtils::quoteString(mailboxName); + + if (!flags.empty()) { + + cmd << " ("; + + for (size_t i = 0, n = flags.size() ; i < n ; ++i) { + if (i != 0) cmd << " "; + cmd << flags[i]; + } + + cmd << ")"; + } + + if (date != NULL) { + cmd << " " << IMAPUtils::dateTime(*date); + } + + cmd << " {" << size << "}"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::COPY( + const messageSet& msgs, + const string& mailboxName +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) { + cmd << "UID COPY " << IMAPUtils::messageSetToSequenceSet(msgs); + } else { + cmd << "COPY " << IMAPUtils::messageSetToSequenceSet(msgs); + } + + cmd << " " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::SEARCH( + const std::vector <string>& keys, + const vmime::charset* charset +) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "SEARCH"; + + if (charset) { + cmd << " CHARSET " << charset->getName(); + } + + for (size_t i = 0, n = keys.size() ; i < n ; ++i) { + cmd << " " << keys[i]; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::STARTTLS() { + + return createCommand("STARTTLS"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::CAPABILITY() { + + return createCommand("CAPABILITY"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::NOOP() { + + return createCommand("NOOP"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::EXPUNGE() { + + return createCommand("EXPUNGE"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::CLOSE() { + + return createCommand("CLOSE"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::LOGOUT() { + + return createCommand("LOGOUT"); +} + + +// static +shared_ptr <IMAPCommand> IMAPCommand::createCommand( + const string& text, + const string& traceText +) { + + if (traceText.empty()) { + return shared_ptr <IMAPCommand>(new IMAPCommand(text, text)); + } else { + return shared_ptr <IMAPCommand>(new IMAPCommand(text, traceText)); + } +} + + +const string IMAPCommand::getText() const { + + return m_text; +} + + +const string IMAPCommand::getTraceText() const { + + return m_traceText; +} + + +void IMAPCommand::send(const shared_ptr <IMAPConnection>& conn) { + + conn->sendCommand(dynamicCast <IMAPCommand>(shared_from_this())); +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP diff --git a/vmime-master/src/vmime/net/imap/IMAPCommand.hpp b/vmime-master/src/vmime/net/imap/IMAPCommand.hpp new file mode 100644 index 0000000..4915a57 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPCommand.hpp @@ -0,0 +1,124 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" +#include "vmime/dateTime.hpp" + +#include "vmime/net/messageSet.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPConnection; + + +/** An IMAP command that will be sent to the server. + */ +class VMIME_EXPORT IMAPCommand : public object, public enable_shared_from_this <IMAPCommand> { + +public: + + static shared_ptr <IMAPCommand> LOGIN(const string& username, const string& password); + static shared_ptr <IMAPCommand> AUTHENTICATE(const string& mechName); + static shared_ptr <IMAPCommand> AUTHENTICATE(const string& mechName, const string& initialResponse); + static shared_ptr <IMAPCommand> LIST(const string& refName, const string& mailboxName); + static shared_ptr <IMAPCommand> SELECT(const bool readOnly, const string& mailboxName, const std::vector <string>& params); + static shared_ptr <IMAPCommand> STATUS(const string& mailboxName, const std::vector <string>& attribs); + static shared_ptr <IMAPCommand> CREATE(const string& mailboxName, const std::vector <string>& params); + static shared_ptr <IMAPCommand> DELETE(const string& mailboxName); + static shared_ptr <IMAPCommand> RENAME(const string& mailboxName, const string& newMailboxName); + static shared_ptr <IMAPCommand> FETCH(const messageSet& msgs, const std::vector <string>& params); + static shared_ptr <IMAPCommand> STORE(const messageSet& msgs, const int mode, const std::vector <string>& flags); + static shared_ptr <IMAPCommand> APPEND(const string& mailboxName, const std::vector <string>& flags, vmime::datetime* date, const size_t size); + static shared_ptr <IMAPCommand> COPY(const messageSet& msgs, const string& mailboxName); + static shared_ptr <IMAPCommand> SEARCH(const std::vector <string>& keys, const vmime::charset* charset); + static shared_ptr <IMAPCommand> STARTTLS(); + static shared_ptr <IMAPCommand> CAPABILITY(); + static shared_ptr <IMAPCommand> NOOP(); + static shared_ptr <IMAPCommand> EXPUNGE(); + static shared_ptr <IMAPCommand> CLOSE(); + static shared_ptr <IMAPCommand> LOGOUT(); + + /** Creates a new IMAP command with the specified text. + * + * @param text command text + * @param traceText trace text (if empty, command text is used) + * @return a new IMAPCommand object + */ + static shared_ptr <IMAPCommand> createCommand(const string& text, const string& traceText = ""); + + /** Sends this command over the specified connection. + * + * @param conn connection onto which the command will be sent + */ + virtual void send(const shared_ptr <IMAPConnection>& conn); + + /** Returns the full text of the command, including command name + * and parameters (if any). This is the text that will be sent + * to the server. + * + * @return command text (eg. "LOGIN myusername mypassword") + */ + virtual const string getText() const; + + /** Returns the full text of the command, suitable for outputing + * to the tracer. + * + * @return trace text (eg. "LOGIN {username} {password}") + */ + virtual const string getTraceText() const; + +protected: + + IMAPCommand(const string& text, const string& traceText); + IMAPCommand(const IMAPCommand&); + +private: + + string m_text; + string m_traceText; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPConnection.cpp b/vmime-master/src/vmime/net/imap/IMAPConnection.cpp new file mode 100644 index 0000000..df3da1c --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPConnection.cpp @@ -0,0 +1,886 @@ +// +// 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/IMAPTag.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPUtils.hpp" +#include "vmime/net/imap/IMAPStore.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/SASLContext.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" + #include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + +#include <sstream> + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (m_store.lock()->getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (m_store.lock()->getInfos().hasProperty(getSession(), \ + dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPConnection::IMAPConnection( + const shared_ptr <IMAPStore>& store, + const shared_ptr <security::authenticator>& auth +) + : m_store(store), + m_auth(auth), + m_socket(null), + m_parser(null), + m_tag(null), + m_hierarchySeparator('\0'), + m_state(STATE_NONE), + m_timeoutHandler(null), + m_secured(false), + m_firstTag(true), + m_capabilitiesFetched(false), + m_noModSeq(false) { + + static int connectionId = 0; + + m_tag = make_shared <IMAPTag>(); + + if (store->getTracerFactory()) { + m_tracer = store->getTracerFactory()->create(store, ++connectionId); + } + + m_parser = make_shared <IMAPParser>(); + m_parser->setTracer(m_tracer); +} + + +IMAPConnection::~IMAPConnection() { + + try { + + if (isConnected()) { + disconnect(); + } else if (m_socket) { + internalDisconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void IMAPConnection::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + m_state = STATE_NONE; + m_hierarchySeparator = '\0'; + + const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS); + const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT); + + shared_ptr <IMAPStore> store = m_store.lock(); + + // Create the time-out handler + if (store->getTimeoutHandlerFactory()) { + m_timeoutHandler = store->getTimeoutHandlerFactory()->create(); + } + + // Create and connect the socket + m_socket = store->getSocketFactory()->create(m_timeoutHandler); + m_socket->setTracer(m_tracer); + +#if VMIME_HAVE_TLS_SUPPORT + if (store->isIMAPS()) { // dedicated port/IMAPS + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create + (store->getCertificateVerifier(), + store->getSession()->getTLSProperties()); + + shared_ptr <tls::TLSSocket> tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket); + + } else +#endif // VMIME_HAVE_TLS_SUPPORT + { + m_cntInfos = make_shared <defaultConnectionInfos>(address, port); + } + + m_socket->connect(address, port); + + + m_parser->setSocket(m_socket); + m_parser->setTimeoutHandler(m_timeoutHandler); + + + setState(STATE_NON_AUTHENTICATED); + + + // Connection greeting + // + // eg: C: <connection to server> + // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready + + scoped_ptr <IMAPParser::greeting> greet(m_parser->readGreeting()); + bool needAuth = false; + + if (greet->resp_cond_bye) { + + internalDisconnect(); + throw exceptions::connection_greeting_error(greet->getErrorLog()); + + } else if (greet->resp_cond_auth->condition != IMAPParser::resp_cond_auth::PREAUTH) { + + needAuth = true; + } + + if (greet->resp_cond_auth->resp_text->resp_text_code && + greet->resp_cond_auth->resp_text->resp_text_code->capability_data) { + + processCapabilityResponseData(greet->resp_cond_auth->resp_text->resp_text_code->capability_data.get()); + } + +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!store->isIMAPS() && tls) { // only if not IMAPS + + try { + + startTLS(); + + // Non-fatal error + } catch (exceptions::command_error&) { + + if (tlsRequired) { + m_state = STATE_NONE; + throw; + } else { + // TLS is not required, so don't bother + } + + // Fatal error + } catch (...) { + + m_state = STATE_NONE; + throw; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Authentication + if (needAuth) { + + try { + + authenticate(); + + } catch (...) { + + m_state = STATE_NONE; + throw; + } + } + + // Get the hierarchy separator character + initHierarchySeparator(); + + // Switch to state "Authenticated" + setState(STATE_AUTHENTICATED); +} + + +void IMAPConnection::authenticate() { + + getAuthenticator()->setService(m_store.lock()); + +#if VMIME_HAVE_SASL_SUPPORT + // First, try SASL authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) { + + try { + + authenticateSASL(); + return; + + } catch (exceptions::authentication_error&) { + + if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) { + + // Can't fallback on normal authentication + internalDisconnect(); + throw; + + } else { + + // Ignore, will try normal authentication + } + + } catch (exception&) { + + internalDisconnect(); + throw; + } + } +#endif // VMIME_HAVE_SASL_SUPPORT + + // Normal authentication + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); + + shared_ptr <IMAPConnection> conn = dynamicCast <IMAPConnection>(shared_from_this()); + IMAPCommand::LOGIN(username, password)->send(conn); + + scoped_ptr <IMAPParser::response> resp(m_parser->readResponse(*m_tag)); + + if (resp->isBad()) { + + internalDisconnect(); + throw exceptions::command_error("LOGIN", resp->getErrorLog()); + + } else if (resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + internalDisconnect(); + throw exceptions::authentication_error(resp->getErrorLog()); + } + + // Server capabilities may change when logged in + if (!processCapabilityResponseData(resp.get())) { + invalidateCapabilities(); + } +} + + +#if VMIME_HAVE_SASL_SUPPORT + +void IMAPConnection::authenticateSASL() { + + if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) { + throw exceptions::authentication_error("No SASL authenticator available."); + } + + const std::vector <string> capa = getCapabilities(); + std::vector <string> saslMechs; + + for (unsigned int i = 0 ; i < capa.size() ; ++i) { + + const string& x = capa[i]; + + if (x.length() > 5 && + (x[0] == 'A' || x[0] == 'a') && + (x[1] == 'U' || x[1] == 'u') && + (x[2] == 'T' || x[2] == 't') && + (x[3] == 'H' || x[3] == 'h') && + x[4] == '=') { + + saslMechs.push_back(string(x.begin() + 5, x.end())); + } + } + + if (saslMechs.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; + + shared_ptr <security::sasl::SASLContext> saslContext = + security::sasl::SASLContext::create(); + + for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { + + try { + mechList.push_back(saslContext->createMechanism(saslMechs[i])); + } catch (exceptions::no_such_mechanism&) { + // Ignore mechanism + } + } + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try to suggest a mechanism among all those supported + shared_ptr <security::sasl::SASLMechanism> suggestedMech = + saslContext->suggestMechanism(mechList); + + if (!suggestedMech) { + throw exceptions::authentication_error("Unable to suggest SASL mechanism."); + } + + // Allow application to choose which mechanisms to use + mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> + getAcceptableMechanisms(mechList, suggestedMech); + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try each mechanism in the list in turn + for (unsigned int i = 0 ; i < mechList.size() ; ++i) { + + shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; + + shared_ptr <security::sasl::SASLSession> saslSession = + saslContext->createSession("imap", getAuthenticator(), mech); + + saslSession->init(); + + shared_ptr <IMAPCommand> authCmd; + + if (saslSession->getMechanism()->hasInitialResponse()) { + + byte_t* initialResp = 0; + size_t initialRespLen = 0; + + saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); + + string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); + delete [] initialResp; + + if (encodedInitialResp.empty()) { + authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), "="); + } else { + authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), encodedInitialResp); + } + + } else { + + authCmd = IMAPCommand::AUTHENTICATE(mech->getName()); + } + + authCmd->send(dynamicCast <IMAPConnection>(shared_from_this())); + + for (bool cont = true ; cont ; ) { + + scoped_ptr <IMAPParser::response> resp(m_parser->readResponse(*m_tag)); + + if (resp->response_done && + resp->response_done->response_tagged && + resp->response_done->response_tagged->resp_cond_state-> + status == IMAPParser::resp_cond_state::OK) { + + m_socket = saslSession->getSecuredSocket(m_socket); + return; + + } else { + + string response; + bool hasResponse = false; + + for (auto &respData : resp->continue_req_or_response_data) { + + if (respData->continue_req) { + + response = respData->continue_req->resp_text->text; + hasResponse = true; + break; + } + } + + if (!hasResponse) { + cont = false; + continue; + } + + byte_t* challenge = 0; + size_t challengeLen = 0; + + byte_t* resp = 0; + size_t respLen = 0; + + try { + + // Extract challenge + saslContext->decodeB64(response, &challenge, &challengeLen); + + // Prepare response + saslSession->evaluateChallenge + (challenge, challengeLen, &resp, &respLen); + + // Send response + const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; + sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); + + if (m_tracer) { + m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); + } + + // Server capabilities may change when logged in + invalidateCapabilities(); + + } catch (exceptions::sasl_exception& e) { + + if (challenge) { + delete [] challenge; + challenge = NULL; + } + + if (resp) { + delete [] resp; + resp = NULL; + } + + // Cancel SASL exchange + sendRaw(utility::stringUtils::bytesFromString("*\r\n"), 3); + + if (m_tracer) { + m_tracer->traceSend("*"); + } + + } catch (...) { + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + throw; + } + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + } + } + } + + throw exceptions::authentication_error("Could not authenticate using SASL: all mechanisms failed."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +#if VMIME_HAVE_TLS_SUPPORT + +void IMAPConnection::startTLS() { + + try { + + IMAPCommand::STARTTLS()->send(dynamicCast <IMAPConnection>(shared_from_this())); + + scoped_ptr <IMAPParser::response> resp(m_parser->readResponse(*m_tag)); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("STARTTLS", resp->getErrorLog(), "bad response"); + } + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + m_store.lock()->getCertificateVerifier(), + m_store.lock()->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + tlsSocket->handshake(); + + m_socket = tlsSocket; + m_parser->setSocket(m_socket); + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>( + m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket + ); + + // " Once TLS has been started, the client MUST discard cached + // information about server capabilities and SHOULD re-issue the + // CAPABILITY command. This is necessary to protect against + // man-in-the-middle attacks which alter the capabilities list prior + // to STARTTLS. " (RFC-2595) + invalidateCapabilities(); + + } catch (exceptions::command_error&) { + + // Non-fatal error + throw; + + } catch (exception&) { + + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +const std::vector <string> IMAPConnection::getCapabilities() { + + if (!m_capabilitiesFetched) { + fetchCapabilities(); + } + + return m_capabilities; +} + + +bool IMAPConnection::hasCapability(const string& capa) { + + if (!m_capabilitiesFetched) { + fetchCapabilities(); + } + + const string normCapa = utility::stringUtils::toUpper(capa); + + for (size_t i = 0, n = m_capabilities.size() ; i < n ; ++i) { + + if (m_capabilities[i] == normCapa) { + return true; + } + } + + return false; +} + + +bool IMAPConnection::hasCapability(const string& capa) const { + + const string normCapa = utility::stringUtils::toUpper(capa); + + for (size_t i = 0, n = m_capabilities.size() ; i < n ; ++i) { + + if (m_capabilities[i] == normCapa) + return true; + } + + return false; +} + + +void IMAPConnection::invalidateCapabilities() { + + m_capabilities.clear(); + m_capabilitiesFetched = false; +} + + +void IMAPConnection::fetchCapabilities() { + + IMAPCommand::CAPABILITY()->send(dynamicCast <IMAPConnection>(shared_from_this())); + + scoped_ptr <IMAPParser::response> resp(m_parser->readResponse(*m_tag)); + + if (resp->response_done->response_tagged-> + resp_cond_state->status == IMAPParser::resp_cond_state::OK) { + + processCapabilityResponseData(resp.get()); + } +} + + +bool IMAPConnection::processCapabilityResponseData(const IMAPParser::response* resp) { + + for (auto &respData : resp->continue_req_or_response_data) { + + if (respData->response_data == NULL) { + continue; + } + + auto &capaData = respData->response_data->capability_data; + + if (!capaData) { + continue; + } + + processCapabilityResponseData(capaData.get()); + return true; + } + + return false; +} + + +void IMAPConnection::processCapabilityResponseData(const IMAPParser::capability_data* capaData) { + + std::vector <string> res; + + for (auto &cap : capaData->capabilities) { + + if (cap->auth_type) { + res.push_back("AUTH=" + cap->auth_type->name); + } else { + res.push_back(utility::stringUtils::toUpper(cap->atom->value)); + } + } + + m_capabilities = res; + m_capabilitiesFetched = true; +} + + +shared_ptr <security::authenticator> IMAPConnection::getAuthenticator() { + + return m_auth; +} + + +bool IMAPConnection::isConnected() const { + + return m_socket + && m_socket->isConnected() + && (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED); +} + + +bool IMAPConnection::isSecuredConnection() const { + + return m_secured; +} + + +shared_ptr <connectionInfos> IMAPConnection::getConnectionInfos() const { + + return m_cntInfos; +} + + +void IMAPConnection::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + internalDisconnect(); +} + + +void IMAPConnection::internalDisconnect() { + + if (isConnected()) { + + IMAPCommand::LOGOUT()->send(dynamicCast <IMAPConnection>(shared_from_this())); + + m_socket->disconnect(); + m_socket = null; + } + + m_timeoutHandler = null; + + m_state = STATE_LOGOUT; + + m_secured = false; + m_cntInfos = null; +} + + +void IMAPConnection::initHierarchySeparator() { + + IMAPCommand::LIST("", "")->send(dynamicCast <IMAPConnection>(shared_from_this())); + + scoped_ptr <IMAPParser::response> resp(m_parser->readResponse(*m_tag)); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + internalDisconnect(); + throw exceptions::command_error("LIST", resp->getErrorLog(), "bad response"); + } + + const auto& respDataList = resp->continue_req_or_response_data; + + bool found = false; + + for (unsigned int i = 0 ; !found && i < respDataList.size() ; ++i) { + + if (!respDataList[i]->response_data) { + continue; + } + + auto &mailboxData = respDataList[i]->response_data->mailbox_data; + + if (!mailboxData || mailboxData->type != IMAPParser::mailbox_data::LIST) { + continue; + } + + if (mailboxData->mailbox_list->quoted_char != '\0') { + m_hierarchySeparator = mailboxData->mailbox_list->quoted_char; + found = true; + } + } + + if (!found) { // default + m_hierarchySeparator = '/'; + } +} + + +void IMAPConnection::sendCommand(const shared_ptr <IMAPCommand>& cmd) { + + if (!m_firstTag) { + ++(*m_tag); + } + + m_socket->send(*m_tag); + m_socket->send(" "); + m_socket->send(cmd->getText()); + m_socket->send("\r\n"); + + m_firstTag = false; + + if (m_tracer) { + + std::ostringstream oss; + oss << string(*m_tag) << " " << cmd->getText(); + + m_tracer->traceSend(oss.str()); + } +} + + +void IMAPConnection::sendRaw(const byte_t* buffer, const size_t count) { + + m_socket->sendRaw(buffer, count); +} + + +IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh) { + + return m_parser->readResponse(*m_tag, lh); +} + + +IMAPConnection::ProtocolStates IMAPConnection::state() const { + + return m_state; +} + + +void IMAPConnection::setState(const ProtocolStates state) { + + m_state = state; +} + + +char IMAPConnection::hierarchySeparator() const { + + return m_hierarchySeparator; +} + + +shared_ptr <const IMAPStore> IMAPConnection::getStore() const { + + return m_store.lock(); +} + + +shared_ptr <IMAPStore> IMAPConnection::getStore() { + + return m_store.lock(); +} + + +shared_ptr <session> IMAPConnection::getSession() { + + return m_store.lock()->getSession(); +} + + +shared_ptr <const socket> IMAPConnection::getSocket() const { + + return m_socket; +} + + +void IMAPConnection::setSocket(const shared_ptr <socket>& sok) { + + m_socket = sok; + m_parser->setSocket(sok); +} + + +shared_ptr <tracer> IMAPConnection::getTracer() { + + return m_tracer; +} + + +shared_ptr <IMAPTag> IMAPConnection::getTag() { + + return m_tag; +} + + +bool IMAPConnection::isMODSEQDisabled() const { + + return m_noModSeq; +} + + +void IMAPConnection::disableMODSEQ() { + + m_noModSeq = true; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPConnection.hpp b/vmime-master/src/vmime/net/imap/IMAPConnection.hpp new file mode 100644 index 0000000..99750d4 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPConnection.hpp @@ -0,0 +1,172 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/tracer.hpp" +#include "vmime/net/session.hpp" +#include "vmime/net/connectionInfos.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + +#include "vmime/security/authenticator.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPTag; +class IMAPStore; +class IMAPCommand; + + +class VMIME_EXPORT IMAPConnection : public object, public enable_shared_from_this <IMAPConnection> { + +public: + + IMAPConnection(const shared_ptr <IMAPStore>& store, const shared_ptr <security::authenticator>& auth); + ~IMAPConnection(); + + + void connect(); + bool isConnected() const; + void disconnect(); + + + enum ProtocolStates { + STATE_NONE, + STATE_NON_AUTHENTICATED, + STATE_AUTHENTICATED, + STATE_SELECTED, + STATE_LOGOUT + }; + + ProtocolStates state() const; + void setState(const ProtocolStates state); + + + char hierarchySeparator() const; + + + void sendCommand(const shared_ptr <IMAPCommand>& cmd); + void sendRaw(const byte_t* buffer, const size_t count); + + IMAPParser::response* readResponse(IMAPParser::literalHandler* lh = NULL); + + + shared_ptr <const IMAPStore> getStore() const; + shared_ptr <IMAPStore> getStore(); + + shared_ptr <session> getSession(); + + void fetchCapabilities(); + void invalidateCapabilities(); + const std::vector <string> getCapabilities(); + bool hasCapability(const string& capa); + bool hasCapability(const string& capa) const; + + shared_ptr <security::authenticator> getAuthenticator(); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + + shared_ptr <const socket> getSocket() const; + void setSocket(const shared_ptr <socket>& sok); + + shared_ptr <tracer> getTracer(); + + shared_ptr <IMAPTag> getTag(); + + bool isMODSEQDisabled() const; + void disableMODSEQ(); + +private: + + void authenticate(); +#if VMIME_HAVE_SASL_SUPPORT + void authenticateSASL(); +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + + bool processCapabilityResponseData(const IMAPParser::response* resp); + void processCapabilityResponseData(const IMAPParser::capability_data* capaData); + + + weak_ptr <IMAPStore> m_store; + + shared_ptr <security::authenticator> m_auth; + + shared_ptr <socket> m_socket; + + shared_ptr <IMAPParser> m_parser; + + shared_ptr <IMAPTag> m_tag; + + char m_hierarchySeparator; + + ProtocolStates m_state; + + shared_ptr <timeoutHandler> m_timeoutHandler; + + bool m_secured; + shared_ptr <connectionInfos> m_cntInfos; + + bool m_firstTag; + + std::vector <string> m_capabilities; + bool m_capabilitiesFetched; + + bool m_noModSeq; + + shared_ptr <tracer> m_tracer; + + + void internalDisconnect(); + + void initHierarchySeparator(); +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPFolder.cpp b/vmime-master/src/vmime/net/imap/IMAPFolder.cpp new file mode 100644 index 0000000..98bc05d --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPFolder.cpp @@ -0,0 +1,1622 @@ +// +// 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/IMAPFolder.hpp" + +#include "vmime/net/imap/IMAPStore.hpp" +#include "vmime/net/imap/IMAPParser.hpp" +#include "vmime/net/imap/IMAPMessage.hpp" +#include "vmime/net/imap/IMAPUtils.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" + +#include "vmime/message.hpp" + +#include "vmime/exception.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" + +#include <algorithm> +#include <sstream> + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPFolder::IMAPFolder( + const folder::path& path, + const shared_ptr <IMAPStore>& store, + const shared_ptr <folderAttributes>& attribs +) + : m_store(store), + m_connection(store->connection()), + m_path(path), + m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), + m_mode(-1), + m_open(false), + m_attribs(attribs) { + + store->registerFolder(this); + + m_status = make_shared <IMAPFolderStatus>(); +} + + +IMAPFolder::~IMAPFolder() { + + try { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (store) { + + if (m_open) { + close(false); + } + + store->unregisterFolder(this); + + } else if (m_open) { + + m_connection = null; + onClose(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +int IMAPFolder::getMode() const { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_mode; +} + + +const folderAttributes IMAPFolder::getAttributes() { + + // Root folder + if (m_path.isEmpty()) { + + folderAttributes attribs; + attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS); + attribs.setFlags(folderAttributes::FLAG_HAS_CHILDREN | folderAttributes::FLAG_NO_OPEN); + + return attribs; + + } else { + + if (!m_attribs) { + testExistAndGetType(); + } + + return *m_attribs; + } +} + + +const folder::path::component IMAPFolder::getName() const { + + return m_name; +} + + +const folder::path IMAPFolder::getFullPath() const { + + return m_path; +} + + +void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + // Ensure this folder is not already open in the same session + for (std::list <IMAPFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == m_path) { + throw exceptions::folder_already_open(); + } + } + + // Open a connection for this folder + shared_ptr <IMAPConnection> connection = + make_shared <IMAPConnection>(store, store->getAuthenticator()); + + try { + + connection->connect(); + + // Emit the "SELECT" command + // + // Example: C: A142 SELECT INBOX + // S: * 172 EXISTS + // S: * 1 RECENT + // S: * OK [UNSEEN 12] Message 12 is first unseen + // S: * OK [UIDVALIDITY 3857529045] UIDs valid + // S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited + // S: A142 OK [READ-WRITE] SELECT completed + + std::vector <string> selectParams; + + if (m_connection->hasCapability("CONDSTORE")) { + selectParams.push_back("CONDSTORE"); + } + + IMAPCommand::SELECT( + mode == MODE_READ_ONLY, + IMAPUtils::pathToString(connection->hierarchySeparator(), getFullPath()), + selectParams + )->send(connection); + + // Read the response + scoped_ptr <IMAPParser::response> resp(connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("SELECT", resp->getErrorLog(), "bad response"); + } + + auto &respDataList = resp->continue_req_or_response_data; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + auto *responseData = (*it)->response_data.get(); + + if (!responseData) { + throw exceptions::command_error("SELECT", resp->getErrorLog(), "invalid response"); + } + + // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional) + if (responseData->resp_cond_state) { + + auto *code = responseData->resp_cond_state->resp_text->resp_text_code.get(); + + if (code) { + + switch (code->type) { + + case IMAPParser::resp_text_code::NOMODSEQ: + + connection->disableMODSEQ(); + break; + + default: + + break; + } + } + + // Untagged responses: FLAGS, EXISTS, RECENT (required) + } else if (responseData->mailbox_data) { + + switch (responseData->mailbox_data->type) { + + default: break; + + case IMAPParser::mailbox_data::FLAGS: { + + if (!m_attribs) { + m_attribs = make_shared <folderAttributes>(); + } + + IMAPUtils::mailboxFlagsToFolderAttributes( + connection, + m_path, + *responseData->mailbox_data->mailbox_flag_list, + *m_attribs + ); + + break; + } + } + } + } + + processStatusUpdate(resp.get()); + + // Check for access mode (read-only or read-write) + auto *respTextCode = resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get(); + + if (respTextCode) { + + const int openMode = + (respTextCode->type == IMAPParser::resp_text_code::READ_WRITE) + ? MODE_READ_WRITE + : MODE_READ_ONLY; + + if (failIfModeIsNotAvailable && + mode == MODE_READ_WRITE && openMode == MODE_READ_ONLY) { + + throw exceptions::operation_not_supported(); + } + } + + m_connection = connection; + m_open = true; + m_mode = mode; + + } catch (std::exception&) { + + throw; + } +} + + +void IMAPFolder::close(const bool expunge) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + shared_ptr <IMAPConnection> oldConnection = m_connection; + + // Emit the "CLOSE" command to expunge messages marked + // as deleted (this is fastest than "EXPUNGE") + if (expunge) { + + if (m_mode == MODE_READ_ONLY) { + throw exceptions::operation_not_supported(); + } + + IMAPCommand::CLOSE()->send(oldConnection); + } + + // Close this folder connection + oldConnection->disconnect(); + + // Now use default store connection + m_connection = m_store.lock()->connection(); + + m_open = false; + m_mode = -1; + + m_status = make_shared <IMAPFolderStatus>(); + + onClose(); +} + + +void IMAPFolder::onClose() { + + for (std::vector <IMAPMessage*>::iterator it = m_messages.begin() ; + it != m_messages.end() ; ++it) { + + (*it)->onFolderClosed(); + } + + m_messages.clear(); +} + + +void IMAPFolder::create(const folderAttributes& attribs) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (isOpen()) { + throw exceptions::illegal_state("Folder is open"); + } else if (exists()) { + throw exceptions::illegal_state("Folder already exists"); + } else if (!store->isValidFolderName(m_name)) { + throw exceptions::invalid_folder_name(); + } + + // Emit the "CREATE" command + // + // Example: C: A003 CREATE owatagusiam/ + // S: A003 OK CREATE completed + // C: A004 CREATE owatagusiam/blurdybloop + // S: A004 OK CREATE completed + + string mailbox = IMAPUtils::pathToString + (m_connection->hierarchySeparator(), getFullPath()); + + if (attribs.getType() & folderAttributes::TYPE_CONTAINS_FOLDERS) { + mailbox += m_connection->hierarchySeparator(); + } + + std::vector <string> createParams; + + if (attribs.getSpecialUse() != folderAttributes::SPECIALUSE_NONE) { + + if (!m_connection->hasCapability("CREATE-SPECIAL-USE")) { + throw exceptions::operation_not_supported(); + } + + // C: t2 CREATE MySpecial (USE (\Drafts \Sent)) + std::ostringstream oss; + oss << "USE ("; + + switch (attribs.getSpecialUse()) { + + case folderAttributes::SPECIALUSE_NONE: // should not happen + case folderAttributes::SPECIALUSE_ALL: oss << "\\All"; break; + case folderAttributes::SPECIALUSE_ARCHIVE: oss << "\\Archive"; break; + case folderAttributes::SPECIALUSE_DRAFTS: oss << "\\Drafts"; break; + case folderAttributes::SPECIALUSE_FLAGGED: oss << "\\Flagged"; break; + case folderAttributes::SPECIALUSE_JUNK: oss << "\\Junk"; break; + case folderAttributes::SPECIALUSE_SENT: oss << "\\Sent"; break; + case folderAttributes::SPECIALUSE_TRASH: oss << "\\Trash"; break; + case folderAttributes::SPECIALUSE_IMPORTANT: oss << "\\Important"; break; + } + + oss << ")"; + + createParams.push_back(oss.str()); + } + + IMAPCommand::CREATE(mailbox, createParams)->send(m_connection); + + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("CREATE", resp->getErrorLog(), "bad response"); + } + + // Notify folder created + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_CREATED, + m_path, m_path + ); + + notifyFolder(event); +} + + +void IMAPFolder::destroy() { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (isOpen()) { + throw exceptions::illegal_state("Folder is open"); + } + + const string mailbox = IMAPUtils::pathToString( + m_connection->hierarchySeparator(), getFullPath() + ); + + IMAPCommand::DELETE(mailbox)->send(m_connection); + + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("DELETE", resp->getErrorLog(), "bad response"); + } + + // Notify folder deleted + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_DELETED, + m_path, m_path + ); + + notifyFolder(event); +} + + +bool IMAPFolder::exists() { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!isOpen() && !store) { + throw exceptions::illegal_state("Store disconnected"); + } + + return testExistAndGetType() != -1; +} + + +int IMAPFolder::testExistAndGetType() { + + // To test whether a folder exists, we simple list it using + // the "LIST" command, and there should be one unique mailbox + // with this name... + // + // Eg. Test whether '/foo/bar' exists + // + // C: a005 list "" foo/bar + // S: * LIST (\NoSelect) "/" foo/bar + // S: a005 OK LIST completed + // + // ==> OK, exists + // + // Test whether '/foo/bar/zap' exists + // + // C: a005 list "" foo/bar/zap + // S: a005 OK LIST completed + // + // ==> NO, does not exist + + IMAPCommand::LIST( + "", + IMAPUtils::pathToString( + m_connection->hierarchySeparator(), + getFullPath() + ) + )->send(m_connection); + + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("LIST", resp->getErrorLog(), "bad response"); + } + + // Check whether the result mailbox list contains this folder + auto& respDataList = resp->continue_req_or_response_data; + + folderAttributes attribs; + attribs.setType(-1); + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("LIST", resp->getErrorLog(), "invalid response"); + } + + auto *mailboxData = (*it)->response_data->mailbox_data.get(); + + // We are only interested in responses of type "LIST" + if (mailboxData && + mailboxData->type == IMAPParser::mailbox_data::LIST) { + + // Get the folder type/flags at the same time + IMAPUtils::mailboxFlagsToFolderAttributes( + m_connection, + m_path, + *mailboxData->mailbox_list->mailbox_flag_list, + attribs + ); + } + } + + m_attribs = make_shared <folderAttributes>(attribs); + + return m_attribs->getType(); +} + + +bool IMAPFolder::isOpen() const { + + return m_open; +} + + +shared_ptr <message> IMAPFolder::getMessage(const size_t num) { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (num < 1 || num > m_status->getMessageCount()) { + throw exceptions::message_not_found(); + } + + return make_shared <IMAPMessage>(dynamicCast <IMAPFolder>(shared_from_this()), num); +} + + +std::vector <shared_ptr <message> > IMAPFolder::getMessages(const messageSet& msgs) { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msgs.isEmpty()) { + return std::vector <shared_ptr <message> >(); + } + + std::vector <shared_ptr <message> > messages; + + // Sequence number message set: + // C: . FETCH uuuu1,uuuu2,uuuu3 UID + // S: * nnnn1 FETCH (UID uuuu1) + // S: * nnnn2 FETCH (UID uuuu2) + // S: * nnnn3 FETCH (UID uuuu3) + // S: . OK FETCH completed + + // UID message set: + // C: . UID FETCH uuuu1,uuuu2,uuuu3 UID + // S: * nnnn1 FETCH (UID uuuu1) + // S: * nnnn2 FETCH (UID uuuu2) + // S: * nnnn3 FETCH (UID uuuu3) + // S: . OK UID FETCH completed + + std::vector <string> params; + params.push_back("UID"); + + IMAPCommand::FETCH(msgs, params)->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "bad response"); + } + + // Process the response + auto &respDataList = resp->continue_req_or_response_data; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("UID FETCH ... UID", resp->getErrorLog(), "invalid response"); + } + + auto *messageData = (*it)->response_data->message_data.get(); + + // We are only interested in responses of type "FETCH" + if (!messageData || messageData->type != IMAPParser::message_data::FETCH) { + continue; + } + + // Find UID in message attributes + const size_t msgNum = messageData->number; + message::uid msgUID; + + for (auto &att : messageData->msg_att->items) { + + if (att->type == IMAPParser::msg_att_item::UID) { + msgUID = att->uniqueid->value; + break; + } + } + + if (!msgUID.empty()) { + shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this()); + messages.push_back(make_shared <IMAPMessage>(thisFolder, msgNum, msgUID)); + } + } + + return messages; +} + + +size_t IMAPFolder::getMessageCount() { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_status->getMessageCount(); +} + + +vmime_uint32 IMAPFolder::getUIDValidity() const { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_status->getUIDValidity(); +} + + +vmime_uint64 IMAPFolder::getHighestModSequence() const { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_status->getHighestModSeq(); +} + + +shared_ptr <folder> IMAPFolder::getFolder(const folder::path::component& name) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + return make_shared <IMAPFolder>(m_path / name, store, shared_ptr <folderAttributes>()); +} + + +std::vector <shared_ptr <folder> > IMAPFolder::getFolders(const bool recursive) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!isOpen() && !store) { + throw exceptions::illegal_state("Store disconnected"); + } + + // Eg. List folders in '/foo/bar' + // + // C: a005 list "foo/bar" * + // S: * LIST (\NoSelect) "/" foo/bar + // S: * LIST (\NoInferiors) "/" foo/bar/zap + // S: a005 OK LIST completed + + shared_ptr <IMAPCommand> cmd; + + const string pathString = IMAPUtils::pathToString( + m_connection->hierarchySeparator(), getFullPath() + ); + + if (recursive) { + + cmd = IMAPCommand::LIST(pathString, "*"); + + } else { + + cmd = IMAPCommand::LIST( + pathString.empty() + ? "" + : (pathString + m_connection->hierarchySeparator()), + "%" + ); + } + + cmd->send(m_connection); + + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("LIST", resp->getErrorLog(), "bad response"); + } + + auto &respDataList = resp->continue_req_or_response_data; + + std::vector <shared_ptr <folder> > v; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("LIST", resp->getErrorLog(), "invalid response"); + } + + auto *mailboxData = (*it)->response_data->mailbox_data.get(); + + if (!mailboxData || mailboxData->type != IMAPParser::mailbox_data::LIST) { + continue; + } + + // Get folder path + auto &mailbox = mailboxData->mailbox_list->mailbox; + + folder::path path = IMAPUtils::stringToPath( + mailboxData->mailbox_list->quoted_char, mailbox->name + ); + + if (recursive || m_path.isDirectParentOf(path)) { + + // Append folder to list + shared_ptr <folderAttributes> attribs = make_shared <folderAttributes>(); + + IMAPUtils::mailboxFlagsToFolderAttributes( + m_connection, + path, + *mailboxData->mailbox_list->mailbox_flag_list, + *attribs + ); + + v.push_back(make_shared <IMAPFolder>(path, store, attribs)); + } + } + + return v; +} + + +void IMAPFolder::fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress +) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msg.empty()) { + return; + } + + // Build message numbers list + std::vector <size_t> list; + list.reserve(msg.size()); + + std::map <size_t, shared_ptr <IMAPMessage> > numberToMsg; + + for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; it != msg.end() ; ++it) { + + list.push_back((*it)->getNumber()); + numberToMsg[(*it)->getNumber()] = dynamicCast <IMAPMessage>(*it); + } + + // Send the request + IMAPUtils::buildFetchCommand( + m_connection, messageSet::byNumber(list), options + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + 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"); + } + + auto &respDataList = resp->continue_req_or_response_data; + + const size_t total = msg.size(); + size_t current = 0; + + if (progress) { + progress->start(total); + } + + try { + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("FETCH", resp->getErrorLog(), "invalid response"); + } + + auto *messageData = (*it)->response_data->message_data.get(); + + // We are only interested in responses of type "FETCH" + if (!messageData || messageData->type != IMAPParser::message_data::FETCH) { + continue; + } + + // Process fetch response for this message + const size_t num = messageData->number; + + std::map <size_t, shared_ptr <IMAPMessage> >::iterator msg = numberToMsg.find(num); + + if (msg != numberToMsg.end()) { + + (*msg).second->processFetchResponse(options, *messageData); + + if (progress) { + progress->progress(++current, total); + } + } + } + + } catch (...) { + + if (progress) { + progress->stop(total); + } + + throw; + } + + if (progress) { + progress->stop(total); + } + + processStatusUpdate(resp.get()); +} + + +void IMAPFolder::fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options) { + + std::vector <shared_ptr <message> > msgs; + msgs.push_back(msg); + + fetchMessages(msgs, options, /* progress */ NULL); +} + + +std::vector <shared_ptr <message> > IMAPFolder::getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs +) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msgs.isEmpty()) { + return std::vector <shared_ptr <message> >(); + } + + // Ensure we also get the UID for each message + fetchAttributes attribsWithUID(attribs); + attribsWithUID.add(fetchAttributes::UID); + + // Send the request + IMAPUtils::buildFetchCommand(m_connection, msgs, attribsWithUID)->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + 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"); + } + + auto &respDataList = resp->continue_req_or_response_data; + + std::vector <shared_ptr <message> > messages; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("FETCH", resp->getErrorLog(), "invalid response"); + } + + auto *messageData = (*it)->response_data->message_data.get(); + + // We are only interested in responses of type "FETCH" + if (!messageData || messageData->type != IMAPParser::message_data::FETCH) { + continue; + } + + // Get message number + const size_t msgNum = messageData->number; + + // Get message UID + message::uid msgUID; + + for (auto &att : messageData->msg_att->items) { + + if (att->type == IMAPParser::msg_att_item::UID) { + msgUID = att->uniqueid->value; + break; + } + } + + // Create a new message reference + shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this()); + shared_ptr <IMAPMessage> msg = make_shared <IMAPMessage>(thisFolder, msgNum, msgUID); + + messages.push_back(msg); + + // Process fetch response for this message + msg->processFetchResponse(attribsWithUID, *messageData); + } + + processStatusUpdate(resp.get()); + + return messages; +} + + +int IMAPFolder::getFetchCapabilities() const { + + return fetchAttributes::ENVELOPE | fetchAttributes::CONTENT_INFO | + fetchAttributes::STRUCTURE | fetchAttributes::FLAGS | + fetchAttributes::SIZE | fetchAttributes::FULL_HEADER | + fetchAttributes::UID | fetchAttributes::IMPORTANCE; +} + + +shared_ptr <folder> IMAPFolder::getParent() { + + if (m_path.isEmpty()) { + + return null; + + } else { + + return make_shared <IMAPFolder>( + m_path.getParent(), m_store.lock(), shared_ptr <folderAttributes>() + ); + } +} + + +shared_ptr <const store> IMAPFolder::getStore() const { + + return m_store.lock(); +} + + +shared_ptr <store> IMAPFolder::getStore() { + + return m_store.lock(); +} + + +void IMAPFolder::registerMessage(IMAPMessage* msg) { + + m_messages.push_back(msg); +} + + +void IMAPFolder::unregisterMessage(IMAPMessage* msg) { + + std::vector <IMAPMessage*>::iterator it = + std::find(m_messages.begin(), m_messages.end(), msg); + + if (it != m_messages.end()) { + m_messages.erase(it); + } +} + + +void IMAPFolder::onStoreDisconnected() { + + m_store.reset(); +} + + +void IMAPFolder::deleteMessages(const messageSet& msgs) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (msgs.isEmpty()) { + throw exceptions::invalid_argument(); + } + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + // Send the request + IMAPCommand::STORE( + msgs, message::FLAG_MODE_ADD, + IMAPUtils::messageFlagList(message::FLAG_DELETED) + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("STORE", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(resp.get()); +} + + +void IMAPFolder::setMessageFlags( + const messageSet& msgs, + const int flags, + const int mode +) { + + const std::vector <string> flagList = IMAPUtils::messageFlagList(flags); + + if ((mode == message::FLAG_MODE_SET) || !flagList.empty()) { + + // Send the request + IMAPCommand::STORE(msgs, mode, flagList)->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("STORE", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(resp.get()); + } +} + + +messageSet IMAPFolder::addMessage( + const shared_ptr <vmime::message>& msg, + const int flags, + vmime::datetime* date, + utility::progressListener* progress +) { + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const string& str = oss.str(); + utility::inputStreamStringAdapter strAdapter(str); + + return addMessage(strAdapter, str.length(), flags, date, progress); +} + + +messageSet IMAPFolder::addMessage( + utility::inputStream& is, + const size_t size, + const int flags, + vmime::datetime* date, + utility::progressListener* progress +) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + // Send the request + IMAPCommand::APPEND( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + IMAPUtils::messageFlagList(flags), date, size + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + bool ok = false; + auto &respList = resp->continue_req_or_response_data; + + for (auto it = respList.begin() ; !ok && (it != respList.end()) ; ++it) { + + if ((*it)->continue_req) { + ok = true; + } + } + + if (!ok) { + throw exceptions::command_error("APPEND", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(resp.get()); + + // Send message data + const size_t total = size; + size_t current = 0; + + if (progress) { + progress->start(total); + } + + const size_t blockSize = std::min( + is.getBlockSize(), + static_cast <size_t>(m_connection->getSocket()->getBlockSize()) + ); + + std::vector <byte_t> vbuffer(blockSize); + byte_t* buffer = &vbuffer.front(); + + while (!is.eof()) { + + // Read some data from the input stream + const size_t read = is.read(buffer, blockSize); + current += read; + + // Put read data into socket output stream + m_connection->sendRaw(buffer, read); + + // Notify progress + if (progress) { + progress->progress(current, total); + } + } + + m_connection->sendRaw(utility::stringUtils::bytesFromString("\r\n"), 2); + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(current); + } + + if (progress) { + progress->stop(total); + } + + // Get the response + scoped_ptr <IMAPParser::response> finalResp(m_connection->readResponse()); + + if (finalResp->isBad() || finalResp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("APPEND", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(finalResp.get()); + + auto *respTextCode = + finalResp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get(); + + if (respTextCode && respTextCode->type == IMAPParser::resp_text_code::APPENDUID) { + return IMAPUtils::buildMessageSet(*respTextCode->uid_set); + } + + return messageSet::empty(); +} + + +void IMAPFolder::expunge() { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + // Send the request + IMAPCommand::EXPUNGE()->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("EXPUNGE", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(resp.get()); +} + + +void IMAPFolder::rename(const folder::path& newPath) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (m_path.isEmpty() || newPath.isEmpty()) { + throw exceptions::illegal_operation("Cannot rename root folder"); + } else if (m_path.getSize() == 1 && m_name.getBuffer() == "INBOX") { + throw exceptions::illegal_operation("Cannot rename 'INBOX' folder"); + } else if (!store->isValidFolderName(newPath.getLastComponent())) { + throw exceptions::invalid_folder_name(); + } + + // Send the request + IMAPCommand::RENAME( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + IMAPUtils::pathToString(m_connection->hierarchySeparator(), newPath) + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("RENAME", resp->getErrorLog(), "bad response"); + } + + // Notify folder renamed + folder::path oldPath(m_path); + + m_path = newPath; + m_name = newPath.getLastComponent(); + + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_RENAMED, + oldPath, + newPath + ); + + notifyFolder(event); + + // Notify sub-folders + for (std::list <IMAPFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && oldPath.isParentOf((*it)->getFullPath())) { + + folder::path oldPath((*it)->m_path); + + (*it)->m_path.renameParent(oldPath, newPath); + + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::folderEvent::TYPE_RENAMED, + oldPath, (*it)->m_path + ); + + (*it)->notifyFolder(event); + } + } + + processStatusUpdate(resp.get()); +} + + +messageSet IMAPFolder::copyMessages(const folder::path& dest, const messageSet& set) { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + // Send the request + IMAPCommand::COPY( + set, + IMAPUtils::pathToString(m_connection->hierarchySeparator(), dest) + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("COPY", resp->getErrorLog(), "bad response"); + } + + processStatusUpdate(resp.get()); + + auto *respTextCode = + resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code.get(); + + if (respTextCode && respTextCode->type == IMAPParser::resp_text_code::COPYUID) { + return IMAPUtils::buildMessageSet(*respTextCode->uid_set2); + } + + return messageSet::empty(); +} + + +void IMAPFolder::status(size_t& count, size_t& unseen) { + + count = 0; + unseen = 0; + + shared_ptr <folderStatus> status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); +} + + +shared_ptr <folderStatus> IMAPFolder::getStatus() { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + // Build the attributes list + std::vector <string> attribs; + + attribs.push_back("MESSAGES"); + attribs.push_back("UNSEEN"); + attribs.push_back("UIDNEXT"); + attribs.push_back("UIDVALIDITY"); + + if (m_connection->hasCapability("CONDSTORE")) { + attribs.push_back("HIGHESTMODSEQ"); + } + + // Send the request + IMAPCommand::STATUS( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + attribs + )->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("STATUS", resp->getErrorLog(), "bad response"); + } + + auto &respDataList = resp->continue_req_or_response_data; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if ((*it)->response_data) { + + auto &responseData = (*it)->response_data; + + if (responseData->mailbox_data && + responseData->mailbox_data->type == IMAPParser::mailbox_data::STATUS) { + + shared_ptr <IMAPFolderStatus> status = make_shared <IMAPFolderStatus>(); + status->updateFromResponse(*responseData->mailbox_data); + + m_status->updateFromResponse(*responseData->mailbox_data); + + return status; + } + } + } + + throw exceptions::command_error("STATUS", resp->getErrorLog(), "invalid response"); +} + + +void IMAPFolder::noop() { + + shared_ptr <IMAPStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + IMAPCommand::NOOP()->send(m_connection); + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("NOOP", resp->getErrorLog()); + } + + processStatusUpdate(resp.get()); +} + + +std::vector <size_t> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid) { + + // Send the request + std::ostringstream uidSearchKey; + uidSearchKey.imbue(std::locale::classic()); + uidSearchKey << "UID " << uid << ":*"; + + std::vector <string> searchKeys; + searchKeys.push_back(uidSearchKey.str()); + + IMAPCommand::SEARCH(searchKeys, /* charset */ NULL)->send(m_connection); + + // Get the response + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || + resp->response_done->response_tagged->resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("SEARCH", resp->getErrorLog(), "bad response"); + } + + auto& respDataList = resp->continue_req_or_response_data; + + std::vector <size_t> seqNumbers; + + for (auto it = respDataList.begin() ; it != respDataList.end() ; ++it) { + + if (!(*it)->response_data) { + throw exceptions::command_error("SEARCH", resp->getErrorLog(), "invalid response"); + } + + auto *mailboxData = (*it)->response_data->mailbox_data.get(); + + // We are only interested in responses of type "SEARCH" + if (!mailboxData || + mailboxData->type != IMAPParser::mailbox_data::SEARCH) { + + continue; + } + + for (auto &nzn : mailboxData->search_nz_number_list) { + seqNumbers.push_back(nzn->value); + } + } + + processStatusUpdate(resp.get()); + + return seqNumbers; +} + + +void IMAPFolder::processStatusUpdate(const IMAPParser::response* resp) { + + std::vector <shared_ptr <events::event> > events; + + shared_ptr <IMAPFolderStatus> oldStatus = vmime::clone(m_status); + int expungedMessageCount = 0; + + // Process tagged response + if (resp->response_done && + resp->response_done->response_tagged && + resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code) { + + m_status->updateFromResponse( + *resp->response_done->response_tagged->resp_cond_state->resp_text->resp_text_code + ); + } + + // Process untagged responses + for (auto it = resp->continue_req_or_response_data.begin() ; + it != resp->continue_req_or_response_data.end() ; ++it) { + + if ((*it)->response_data && + (*it)->response_data->resp_cond_state && + (*it)->response_data->resp_cond_state->resp_text->resp_text_code) { + + m_status->updateFromResponse( + *(*it)->response_data->resp_cond_state->resp_text->resp_text_code + ); + + } else if ((*it)->response_data && + (*it)->response_data->mailbox_data) { + + m_status->updateFromResponse(*(*it)->response_data->mailbox_data); + + // Update folder attributes, if available + if ((*it)->response_data->mailbox_data->type == IMAPParser::mailbox_data::LIST) { + + folderAttributes attribs; + IMAPUtils::mailboxFlagsToFolderAttributes( + m_connection, + m_path, + *(*it)->response_data->mailbox_data->mailbox_list->mailbox_flag_list, + attribs + ); + + m_attribs = make_shared <folderAttributes>(attribs); + } + + } else if ((*it)->response_data && (*it)->response_data->message_data) { + + auto* msgData = (*it)->response_data->message_data.get(); + const size_t msgNumber = msgData->number; + + if (msgData->type == IMAPParser::message_data::FETCH) { + + // Message changed + for (std::vector <IMAPMessage*>::iterator mit = + m_messages.begin() ; mit != m_messages.end() ; ++mit) { + + if ((*mit)->getNumber() == msgNumber) { + (*mit)->processFetchResponse(/* options */ 0, *msgData); + } + } + + events.push_back( + make_shared <events::messageChangedEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageChangedEvent::TYPE_FLAGS, + std::vector <size_t>(1, msgNumber) + ) + ); + + } else if (msgData->type == IMAPParser::message_data::EXPUNGE) { + + // A message has been expunged, renumber messages + for (std::vector <IMAPMessage*>::iterator jt = + m_messages.begin() ; jt != m_messages.end() ; ++jt) { + + if ((*jt)->getNumber() == msgNumber) { + (*jt)->setExpunged(); + } else if ((*jt)->getNumber() > msgNumber) { + (*jt)->renumber((*jt)->getNumber() - 1); + } + } + + events.push_back( + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_REMOVED, + std::vector <size_t>(1, msgNumber) + ) + ); + + expungedMessageCount++; + } + } + } + + // New messages arrived + if (m_status->getMessageCount() > oldStatus->getMessageCount() - expungedMessageCount) { + + std::vector <size_t> newMessageNumbers; + + for (size_t msgNumber = oldStatus->getMessageCount() - expungedMessageCount ; + msgNumber <= m_status->getMessageCount() ; ++msgNumber) { + + newMessageNumbers.push_back(msgNumber); + } + + events.push_back( + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + newMessageNumbers + ) + ); + } + + // Dispatch notifications + for (std::vector <shared_ptr <events::event> >::iterator evit = + events.begin() ; evit != events.end() ; ++evit) { + + notifyEvent(*evit); + } +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPFolder.hpp b/vmime-master/src/vmime/net/imap/IMAPFolder.hpp new file mode 100644 index 0000000..3e6b04b --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPFolder.hpp @@ -0,0 +1,230 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include <vector> +#include <map> + +#include "vmime/types.hpp" + +#include "vmime/net/folder.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPStore; +class IMAPMessage; +class IMAPConnection; +class IMAPFolderStatus; + + +/** IMAP folder implementation. + */ +class VMIME_EXPORT IMAPFolder : public folder { + +private: + + friend class IMAPStore; + friend class IMAPMessage; + + IMAPFolder(const IMAPFolder&); + +public: + + IMAPFolder( + const folder::path& path, + const shared_ptr <IMAPStore>& store, + const shared_ptr <folderAttributes>& attribs + ); + + ~IMAPFolder(); + + int getMode() const; + + const folderAttributes getAttributes(); + + const folder::path::component getName() const; + const folder::path getFullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const folderAttributes& attribs); + + bool exists(); + + void destroy(); + + bool isOpen() const; + + shared_ptr <message> getMessage(const size_t num); + std::vector <shared_ptr <message> > getMessages(const messageSet& msgs); + + std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); + + size_t getMessageCount(); + + shared_ptr <folder> getFolder(const folder::path::component& name); + std::vector <shared_ptr <folder> > getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessages(const messageSet& msgs); + + void setMessageFlags( + const messageSet& msgs, + const int flags, + const int mode = message::FLAG_MODE_SET + ); + + messageSet addMessage( + const shared_ptr <vmime::message>& msg, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet addMessage( + utility::inputStream& is, + const size_t size, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet copyMessages(const folder::path& dest, const messageSet& msgs); + + void status(size_t& count, size_t& unseen); + shared_ptr <folderStatus> getStatus(); + + void noop(); + + void expunge(); + + shared_ptr <folder> getParent(); + + shared_ptr <const store> getStore() const; + shared_ptr <store> getStore(); + + + void fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress = NULL + ); + + void fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options); + + std::vector <shared_ptr <message> > getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs + ); + + int getFetchCapabilities() const; + + /** Returns the UID validity of the folder for the current session. + * If the server is capable of persisting UIDs accross sessions, + * this value should never change for a folder. If the UID validity + * differs across sessions, then the UIDs obtained during a previous + * session may not correspond to the UIDs of the same messages in + * this session. + * + * @return UID validity of the folder + */ + vmime_uint32 getUIDValidity() const; + + /** Returns the highest modification sequence of this folder, ie the + * modification sequence of the last message that changed in this + * folder. + * + * @return modification sequence, or zero if not supported by + * the underlying protocol + */ + vmime_uint64 getHighestModSequence() const; + +private: + + void registerMessage(IMAPMessage* msg); + void unregisterMessage(IMAPMessage* msg); + + void onStoreDisconnected(); + + void onClose(); + + int testExistAndGetType(); + + void setMessageFlagsImpl(const string& set, const int flags, const int mode); + + void copyMessagesImpl(const string& set, const folder::path& dest); + + + /** Process status updates ("unsolicited responses") contained in the + * specified response. Example: + * + * C: a006 NOOP + * S: * 930 EXISTS <-- this is a status update + * S: a006 OK Success + * + * @param resp parsed IMAP response + */ + void processStatusUpdate(const IMAPParser::response* resp); + + + weak_ptr <IMAPStore> m_store; + shared_ptr <IMAPConnection> m_connection; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + shared_ptr <folderAttributes> m_attribs; + + shared_ptr <IMAPFolderStatus> m_status; + + std::vector <IMAPMessage*> m_messages; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPFolderStatus.cpp b/vmime-master/src/vmime/net/imap/IMAPFolderStatus.cpp new file mode 100644 index 0000000..55df337 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPFolderStatus.cpp @@ -0,0 +1,292 @@ +// +// 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/IMAPFolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPFolderStatus::IMAPFolderStatus() + : m_count(0), + m_unseen(0), + m_recent(0), + m_uidValidity(0), + m_uidNext(0), + m_highestModSeq(0) { + +} + + +IMAPFolderStatus::IMAPFolderStatus(const IMAPFolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen), + m_recent(other.m_recent), + m_uidValidity(other.m_uidValidity), + m_uidNext(other.m_uidNext), + m_highestModSeq(other.m_highestModSeq) { + +} + + +size_t IMAPFolderStatus::getMessageCount() const { + + return m_count; +} + + +size_t IMAPFolderStatus::getUnseenCount() const { + + return m_unseen; +} + + +size_t IMAPFolderStatus::getRecentCount() const { + + return m_recent; +} + + +vmime_uint32 IMAPFolderStatus::getUIDValidity() const { + + return m_uidValidity; +} + + +vmime_uint32 IMAPFolderStatus::getUIDNext() const { + + return m_uidNext; +} + + +vmime_uint64 IMAPFolderStatus::getHighestModSeq() const { + + return m_highestModSeq; +} + + +shared_ptr <folderStatus> IMAPFolderStatus::clone() const { + + return make_shared <IMAPFolderStatus>(*this); +} + + +bool IMAPFolderStatus::updateFromResponse(const IMAPParser::mailbox_data& resp) { + + bool changed = false; + + if (resp.type == IMAPParser::mailbox_data::STATUS) { + + for (auto &attval : resp.status_att_list->values) { + + switch (attval->type) { + + case IMAPParser::status_att_val::MESSAGES: { + + const size_t count = + static_cast <size_t>(attval->value_as_number()->value); + + if (m_count != count) { + m_count = count; + changed = true; + } + + break; + } + case IMAPParser::status_att_val::UNSEEN: { + + const size_t unseen = + static_cast <size_t>(attval->value_as_number()->value); + + if (m_unseen != unseen) { + m_unseen = unseen; + changed = true; + } + + break; + } + case IMAPParser::status_att_val::RECENT: { + + const size_t recent = + static_cast <size_t>(attval->value_as_number()->value); + + if (m_recent != recent) { + m_recent = recent; + changed = true; + } + + break; + } + case IMAPParser::status_att_val::UIDNEXT: { + + const vmime_uint32 uidNext = + static_cast <vmime_uint32>(attval->value_as_number()->value); + + if (m_uidNext != uidNext) { + m_uidNext = uidNext; + changed = true; + } + + break; + } + case IMAPParser::status_att_val::UIDVALIDITY: { + + const vmime_uint32 uidValidity = + static_cast <vmime_uint32>(attval->value_as_number()->value); + + if (m_uidValidity != uidValidity) { + m_uidValidity = uidValidity; + changed = true; + } + + break; + } + case IMAPParser::status_att_val::HIGHESTMODSEQ: { + + const vmime_uint64 highestModSeq = + static_cast <vmime_uint64>(attval->value_as_mod_sequence_value()->value); + + if (m_highestModSeq != highestModSeq) { + m_highestModSeq = highestModSeq; + changed = true; + } + + break; + } + + } + } + + } else if (resp.type == IMAPParser::mailbox_data::EXISTS) { + + const size_t count = + static_cast <size_t>(resp.number->value); + + if (m_count != count) { + m_count = count; + changed = true; + } + + } else if (resp.type == IMAPParser::mailbox_data::RECENT) { + + const size_t recent = + static_cast <size_t>(resp.number->value); + + if (m_recent != recent) { + m_recent = recent; + changed = true; + } + } + + return changed; +} + + +bool IMAPFolderStatus::updateFromResponse(const IMAPParser::resp_text_code& resp) { + + bool changed = false; + + switch (resp.type) { + + case IMAPParser::resp_text_code::UIDVALIDITY: { + + const vmime_uint32 uidValidity = + static_cast <vmime_uint32>(resp.nz_number->value); + + if (m_uidValidity != uidValidity) { + m_uidValidity = uidValidity; + changed = true; + } + + break; + } + case IMAPParser::resp_text_code::UIDNEXT: { + + const vmime_uint32 uidNext = + static_cast <vmime_uint32>(resp.nz_number->value); + + if (m_uidNext != uidNext) { + m_uidNext = uidNext; + changed = true; + } + + break; + } + case IMAPParser::resp_text_code::UNSEEN: { + + const size_t unseen = + static_cast <size_t>(resp.nz_number->value); + + if (m_unseen != unseen) + { + m_unseen = unseen; + changed = true; + } + + break; + } + case IMAPParser::resp_text_code::HIGHESTMODSEQ: { + + const vmime_uint64 highestModSeq = + static_cast <vmime_uint64>(resp.mod_sequence_value->value); + + if (m_highestModSeq != highestModSeq) { + m_highestModSeq = highestModSeq; + changed = true; + } + + break; + } + case IMAPParser::resp_text_code::NOMODSEQ: { + + if (m_highestModSeq != 0) { + m_highestModSeq = 0; + changed = true; + } + + break; + } + default: + + break; + } + + return changed; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP diff --git a/vmime-master/src/vmime/net/imap/IMAPFolderStatus.hpp b/vmime-master/src/vmime/net/imap/IMAPFolderStatus.hpp new file mode 100644 index 0000000..b9a73ec --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPFolderStatus.hpp @@ -0,0 +1,123 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/folderStatus.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** Holds the status of an IMAP folder. + */ +class VMIME_EXPORT IMAPFolderStatus : public folderStatus { + +public: + + IMAPFolderStatus(); + IMAPFolderStatus(const IMAPFolderStatus& other); + + // Inherited from folderStatus + size_t getMessageCount() const; + size_t getUnseenCount() const; + + shared_ptr <folderStatus> clone() const; + + /** Returns the the number of messages with the Recent flag set. + * + * @return number of messages flagged Recent + */ + size_t getRecentCount() const; + + /** Returns the UID validity of the folder for the current session. + * If the server is capable of persisting UIDs accross sessions, + * this value should never change for a folder. + * + * @return UID validity of the folder + */ + vmime_uint32 getUIDValidity() const; + + /** Returns the UID value that will be assigned to a new message + * in the folder. If the server does not support the UIDPLUS + * extension, it will return 0. + * + * @return UID of the next message + */ + vmime_uint32 getUIDNext() const; + + /** Returns the highest modification sequence of all messages + * in the folder, or 0 if not available for this folder, or not + * supported by the server. The server must support the CONDSTORE + * extension for this to be available. + * + * @return highest modification sequence + */ + vmime_uint64 getHighestModSeq() const; + + + /** Reads the folder status from the specified IMAP response. + * + * @param resp parsed IMAP response + * @return true if the status changed, or false otherwise + */ + bool updateFromResponse(const IMAPParser::mailbox_data& resp); + + /** Reads the folder status from the specified IMAP response. + * + * @param resp parsed IMAP response + * @return true if the status changed, or false otherwise + */ + bool updateFromResponse(const IMAPParser::resp_text_code& resp); + +private: + + size_t m_count; + size_t m_unseen; + size_t m_recent; + vmime_uint32 m_uidValidity; + vmime_uint32 m_uidNext; + vmime_uint64 m_highestModSeq; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPFOLDERSTATUS_HPP_INCLUDED 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 + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessage.hpp b/vmime-master/src/vmime/net/imap/IMAPMessage.hpp new file mode 100644 index 0000000..6d82a3e --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessage.hpp @@ -0,0 +1,200 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/message.hpp" +#include "vmime/net/folder.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPFolder; + + +/** IMAP message implementation. + */ +class VMIME_EXPORT IMAPMessage : public message { + +private: + + friend class IMAPFolder; + friend class IMAPMessagePartContentHandler; + + IMAPMessage(const IMAPMessage&) : message() { } + +public: + + IMAPMessage(const shared_ptr <IMAPFolder>& folder, const size_t num); + IMAPMessage(const shared_ptr <IMAPFolder>& folder, const size_t num, const uid& uid); + + ~IMAPMessage(); + + size_t getNumber() const; + + const uid getUID() const; + + /** Returns the modification sequence for this message. + * + * Every time metadata for this message changes, the modification + * sequence is updated, and is greater than the previous one. The + * server must support the CONDSTORE extension for this to be + * available. + * + * @return modification sequence, or zero if not supported by + * the underlying protocol + */ + vmime_uint64 getModSequence() const; + + size_t getSize() const; + + bool isExpunged() const; + + shared_ptr <const messageStructure> getStructure() const; + shared_ptr <messageStructure> getStructure(); + + shared_ptr <const header> getHeader() const; + + int getFlags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract( + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void extractPart( + const shared_ptr <const messagePart>& p, + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void fetchPartHeader(const shared_ptr <messagePart>& p); + + shared_ptr <vmime::message> getParsedMessage(); + +private: + + /** Renumbers the message. + * + * @param number new sequence number + */ + void renumber(const size_t number); + + /** Marks the message as expunged. + */ + void setExpunged(); + + /** Processes the parsed response to fill in the attributes + * and metadata of this message. + * + * @param options one or more fetch options (see folder::fetchAttributes) + * @param msgData pointer to message_data component of the parsed response + * @return a combination of flags that specify what changed exactly on + * this message (see events::messageChangedEvent::Types) + */ + int processFetchResponse(const fetchAttributes& options, const IMAPParser::message_data& msgData); + + /** Recursively fetch part header for all parts in the structure. + * + * @param str structure for which to fetch parts headers + */ + void fetchPartHeaderForStructure(const shared_ptr <messageStructure>& str); + + /** Recursively contruct parsed message from structure. + * Called by getParsedMessage(). + * + * @param parentPart root body part (the message) + * @param str structure for which to construct part + * @param level current nesting level (0 is root) + */ + void constructParsedMessage( + const shared_ptr <bodyPart>& parentPart, + const shared_ptr <messageStructure>& str, + int level = 0 + ); + + + enum ExtractFlags + { + EXTRACT_HEADER = 0x1, + EXTRACT_BODY = 0x2, + EXTRACT_PEEK = 0x10 + }; + + size_t 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 <header> getOrCreateHeader(); + + + void onFolderClosed(); + + weak_ptr <IMAPFolder> m_folder; + + size_t m_num; + size_t m_size; + int m_flags; + bool m_expunged; + uid m_uid; + vmime_uint64 m_modseq; + + shared_ptr <header> m_header; + shared_ptr <messageStructure> m_structure; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPMessagePart.cpp b/vmime-master/src/vmime/net/imap/IMAPMessagePart.cpp new file mode 100644 index 0000000..ed2c0bd --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessagePart.cpp @@ -0,0 +1,225 @@ +// +// 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/IMAPMessagePart.hpp" +#include "vmime/net/imap/IMAPMessageStructure.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPMessagePart::IMAPMessagePart( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body_type_mpart* mpart +) + : m_parent(parent), + m_header(null), + m_number(number), + m_size(0) { + + m_mediaType = vmime::mediaType( + "multipart", + mpart->media_subtype->value + ); +} + +namespace { + template<typename T> + vmime::string getPartName(const T& body_type) { + if (const auto* pparam = body_type->body_fields->body_fld_param.get()) { + for (const auto& param : pparam->items) { + if (param->string1->value == "NAME") { + return param->string2->value; + } + } + } + + return {}; + } +} + + +IMAPMessagePart::IMAPMessagePart( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body_type_1part* part +) + : m_parent(parent), + m_header(null), + m_number(number), + m_size(0) { + + if (part->body_type_text) { + + m_mediaType = vmime::mediaType( + "text", + part->body_type_text->media_text->media_subtype->value + ); + + m_size = part->body_type_text->body_fields->body_fld_octets->value; + + m_name = getPartName(part->body_type_text); + + } else if (part->body_type_msg) { + + m_mediaType = vmime::mediaType( + "message", + part->body_type_msg->media_message->media_subtype->value + ); + + } else { + + m_mediaType = vmime::mediaType( + part->body_type_basic->media_basic->media_type->value, + part->body_type_basic->media_basic->media_subtype->value + ); + + m_size = part->body_type_basic->body_fields->body_fld_octets->value; + + m_name = getPartName(part->body_type_basic); + } + + if (part->body_ext_1part && part->body_ext_1part->body_fld_dsp) { + auto *cdisp = part->body_ext_1part->body_fld_dsp->str(); + if (cdisp) { + m_dispType = contentDisposition(cdisp->value); + } + } + + m_structure = null; +} + + +shared_ptr <const messageStructure> IMAPMessagePart::getStructure() const { + + if (m_structure) { + return m_structure; + } else { + return IMAPMessageStructure::emptyStructure(); + } +} + + +shared_ptr <messageStructure> IMAPMessagePart::getStructure() { + + if (m_structure) { + return m_structure; + } else { + return IMAPMessageStructure::emptyStructure(); + } +} + + +shared_ptr <const IMAPMessagePart> IMAPMessagePart::getParent() const { + + return m_parent.lock(); +} + + +const mediaType& IMAPMessagePart::getType() const { + + return m_mediaType; +} + + +const contentDisposition &IMAPMessagePart::getDisposition() const { + + return m_dispType; +} + + +size_t IMAPMessagePart::getSize() const { + + return m_size; +} + + +size_t IMAPMessagePart::getNumber() const { + + return m_number; +} + + +string IMAPMessagePart::getName() const { + + return m_name; +} + + +shared_ptr <const header> IMAPMessagePart::getHeader() const { + + if (!m_header) { + throw exceptions::unfetched_object(); + } else { + return m_header; + } +} + + +// static +shared_ptr <IMAPMessagePart> IMAPMessagePart::create( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body* body +) { + + if (body->body_type_mpart) { + + auto part = make_shared <IMAPMessagePart>(parent, number, body->body_type_mpart.get()); + part->m_structure = make_shared <IMAPMessageStructure>(part, body->body_type_mpart->list); + + return part; + + } else { + + return make_shared <IMAPMessagePart>(parent, number, body->body_type_1part.get()); + } +} + + +header& IMAPMessagePart::getOrCreateHeader() { + + if (m_header) { + return *m_header; + } else { + return *(m_header = make_shared <header>()); + } +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessagePart.hpp b/vmime-master/src/vmime/net/imap/IMAPMessagePart.hpp new file mode 100644 index 0000000..34ea172 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessagePart.hpp @@ -0,0 +1,108 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPMESSAGEPART_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPMESSAGEPART_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/message.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPMessageStructure; + + +class VMIME_EXPORT IMAPMessagePart : public messagePart { + +public: + + IMAPMessagePart( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body_type_mpart* mpart + ); + + IMAPMessagePart( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body_type_1part* part + ); + + shared_ptr <const messageStructure> getStructure() const; + shared_ptr <messageStructure> getStructure(); + + shared_ptr <const IMAPMessagePart> getParent() const; + + const mediaType& getType() const; + const contentDisposition &getDisposition() const; + size_t getSize() const; + size_t getNumber() const; + string getName() const; + + shared_ptr <const header> getHeader() const; + + + static shared_ptr <IMAPMessagePart> create( + const shared_ptr <IMAPMessagePart>& parent, + const size_t number, + const IMAPParser::body* body + ); + + + header& getOrCreateHeader(); + +private: + + shared_ptr <IMAPMessageStructure> m_structure; + weak_ptr <IMAPMessagePart> m_parent; + shared_ptr <header> m_header; + + size_t m_number; + size_t m_size; + string m_name; + mediaType m_mediaType; + contentDisposition m_dispType; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPMESSAGEPART_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp b/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp new file mode 100644 index 0000000..85f6cb6 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp @@ -0,0 +1,227 @@ +// +// 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/IMAPMessagePartContentHandler.hpp" +#include "vmime/net/imap/IMAPFolder.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPStore.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPMessagePartContentHandler::IMAPMessagePartContentHandler( + const shared_ptr <IMAPMessage>& msg, + const shared_ptr <messagePart>& part, + const vmime::encoding& encoding +) + : m_message(msg), + m_part(part), + m_encoding(encoding) { + +} + + +shared_ptr <contentHandler> IMAPMessagePartContentHandler::clone() const { + + return make_shared <IMAPMessagePartContentHandler>( + m_message.lock(), m_part.lock(), m_encoding + ); +} + + +void IMAPMessagePartContentHandler::generate( + utility::outputStream& os, + const vmime::encoding& enc, + const size_t maxLineLength +) const { + + shared_ptr <IMAPMessage> msg = m_message.lock(); + shared_ptr <messagePart> part = m_part.lock(); + + // Data is already encoded + if (isEncoded()) { + + // The data is already encoded but the encoding specified for + // the generation is different from the current one. We need + // to re-encode data: decode from input buffer to temporary + // buffer, and then re-encode to output stream... + if (m_encoding != enc) { + + // Extract part contents to temporary buffer + std::ostringstream oss; + utility::outputStreamAdapter tmp(oss); + + msg->extractPart(part, tmp, NULL); + + // Decode to another temporary buffer + utility::inputStreamStringAdapter in(oss.str()); + + std::ostringstream oss2; + utility::outputStreamAdapter tmp2(oss2); + + shared_ptr <utility::encoder::encoder> theDecoder = m_encoding.getEncoder(); + theDecoder->decode(in, tmp2); + + // Reencode to output stream + string str = oss2.str(); + utility::inputStreamStringAdapter tempIn(str); + + shared_ptr <utility::encoder::encoder> theEncoder = enc.getEncoder(); + theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); + + theEncoder->encode(tempIn, os); + + // No encoding to perform + } else { + + msg->extractPart(part, os); + } + + // Need to encode data before + } else { + + // Extract part contents to temporary buffer + std::ostringstream oss; + utility::outputStreamAdapter tmp(oss); + + msg->extractPart(part, tmp, NULL); + + // Encode temporary buffer to output stream + shared_ptr <utility::encoder::encoder> theEncoder = enc.getEncoder(); + theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); + + utility::inputStreamStringAdapter is(oss.str()); + + theEncoder->encode(is, os); + } +} + + +void IMAPMessagePartContentHandler::extract( + utility::outputStream& os, + utility::progressListener* progress +) const { + + shared_ptr <IMAPMessage> msg = m_message.lock(); + shared_ptr <messagePart> part = m_part.lock(); + + // No decoding to perform + if (!isEncoded()) { + + msg->extractImpl(part, os, progress, 0, -1, IMAPMessage::EXTRACT_BODY); + + // Need to decode data + } else { + + // Extract part contents to temporary buffer + std::ostringstream oss; + utility::outputStreamAdapter tmp(oss); + + msg->extractImpl(part, tmp, NULL, 0, -1, IMAPMessage::EXTRACT_BODY); + + // Encode temporary buffer to output stream + utility::inputStreamStringAdapter is(oss.str()); + utility::progressListenerSizeAdapter plsa(progress, getLength()); + + shared_ptr <utility::encoder::encoder> theDecoder = m_encoding.getEncoder(); + theDecoder->decode(is, os, &plsa); + } +} + + +void IMAPMessagePartContentHandler::extractRaw( + utility::outputStream& os, + utility::progressListener* progress +) const { + + shared_ptr <IMAPMessage> msg = m_message.lock(); + shared_ptr <messagePart> part = m_part.lock(); + + msg->extractPart(part, os, progress); +} + + +size_t IMAPMessagePartContentHandler::getLength() const { + + return m_part.lock()->getSize(); +} + + +bool IMAPMessagePartContentHandler::isEncoded() const { + + return m_encoding != NO_ENCODING; +} + + +const vmime::encoding& IMAPMessagePartContentHandler::getEncoding() const { + + return m_encoding; +} + + +bool IMAPMessagePartContentHandler::isEmpty() const { + + return getLength() == 0; +} + + +bool IMAPMessagePartContentHandler::isBuffered() const { + + return true; +} + + +void IMAPMessagePartContentHandler::setContentTypeHint(const mediaType& type) { + + m_contentType = type; +} + + +const mediaType IMAPMessagePartContentHandler::getContentTypeHint() const { + + return m_contentType; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp b/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp new file mode 100644 index 0000000..17f53e3 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp @@ -0,0 +1,95 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPMESSAGEPARTCONTENTHANDLER_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPMESSAGEPARTCONTENTHANDLER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/contentHandler.hpp" +#include "vmime/net/imap/IMAPMessage.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class VMIME_EXPORT IMAPMessagePartContentHandler : public contentHandler { + +public: + + IMAPMessagePartContentHandler( + const shared_ptr <IMAPMessage>& msg, + const shared_ptr <messagePart>& part, + const vmime::encoding& encoding + ); + + shared_ptr <contentHandler> clone() const; + + void generate( + utility::outputStream& os, + const vmime::encoding& enc, + const size_t maxLineLength = lineLengthLimits::infinite + ) const; + + void extract(utility::outputStream& os, utility::progressListener* progress = NULL) const; + void extractRaw(utility::outputStream& os, utility::progressListener* progress = NULL) const; + + size_t getLength() const; + + bool isEncoded() const; + + const vmime::encoding& getEncoding() const; + + bool isEmpty() const; + + bool isBuffered() const; + + void setContentTypeHint(const mediaType& type); + const mediaType getContentTypeHint() const; + +private: + + weak_ptr <IMAPMessage> m_message; + weak_ptr <messagePart> m_part; + + vmime::encoding m_encoding; + vmime::mediaType m_contentType; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPMESSAGEPARTCONTENTHANDLER_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessageStructure.cpp b/vmime-master/src/vmime/net/imap/IMAPMessageStructure.cpp new file mode 100644 index 0000000..c7ee809 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessageStructure.cpp @@ -0,0 +1,94 @@ +// +// 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/IMAPMessageStructure.hpp" +#include "vmime/net/imap/IMAPMessagePart.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPMessageStructure::IMAPMessageStructure() { +} + + +IMAPMessageStructure::IMAPMessageStructure(const IMAPParser::body* body) { + + m_parts.push_back(IMAPMessagePart::create(null, 0, body)); +} + + +IMAPMessageStructure::IMAPMessageStructure( + const shared_ptr <IMAPMessagePart>& parent, + const std::vector <std::unique_ptr <IMAPParser::body>>& list +) { + + size_t number = 0; + + for (auto it = list.begin() ; it != list.end() ; ++it, ++number) { + m_parts.push_back(IMAPMessagePart::create(parent, number, it->get())); + } +} + + +shared_ptr <const messagePart> IMAPMessageStructure::getPartAt(const size_t x) const { + + return m_parts[x]; +} + + +shared_ptr <messagePart> IMAPMessageStructure::getPartAt(const size_t x) { + + return m_parts[x]; +} + + +size_t IMAPMessageStructure::getPartCount() const { + + return m_parts.size(); +} + + +// static +shared_ptr <IMAPMessageStructure> IMAPMessageStructure::emptyStructure() { + + static shared_ptr <IMAPMessageStructure> emptyStructure = make_shared <IMAPMessageStructure>(); + return emptyStructure; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPMessageStructure.hpp b/vmime-master/src/vmime/net/imap/IMAPMessageStructure.hpp new file mode 100644 index 0000000..ee1eac4 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPMessageStructure.hpp @@ -0,0 +1,75 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPMESSAGESTRUCTURE_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPMESSAGESTRUCTURE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/message.hpp" + +#include "vmime/net/imap/IMAPParser.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPMessagePart; + + +class VMIME_EXPORT IMAPMessageStructure : public messageStructure { + +public: + + IMAPMessageStructure(); + IMAPMessageStructure(const IMAPParser::body* body); + IMAPMessageStructure(const shared_ptr <IMAPMessagePart>& parent, const std::vector <std::unique_ptr <IMAPParser::body>>& list); + + shared_ptr <const messagePart> getPartAt(const size_t x) const; + shared_ptr <messagePart> getPartAt(const size_t x); + size_t getPartCount() const; + + static shared_ptr <IMAPMessageStructure> emptyStructure(); + +private: + + std::vector <shared_ptr <IMAPMessagePart> > m_parts; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPMESSAGESTRUCTURE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/imap/IMAPParser.hpp b/vmime-master/src/vmime/net/imap/IMAPParser.hpp new file mode 100644 index 0000000..281fcb6 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPParser.hpp @@ -0,0 +1,4986 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/base.hpp" +#include "vmime/dateTime.hpp" +#include "vmime/charset.hpp" +#include "vmime/exception.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/progressListener.hpp" + +#include "vmime/utility/encoder/b64Encoder.hpp" +#include "vmime/utility/encoder/qpEncoder.hpp" + +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/net/imap/IMAPTag.hpp" + +#include <vector> +#include <stdexcept> +#include <memory> + + +//#define DEBUG_RESPONSE 1 + + +#if DEBUG_RESPONSE +# include <iostream> +#endif + + +/** Make the parsing of a component fail. + */ +#define VIMAP_PARSER_FAIL() \ + { \ + parser.m_errorResponseLine = makeResponseLine(getComponentName(), line, pos); \ + return false; \ + } + +/** Make the parsing of a component fail if a condition is not matched. + * If the specified expression does not resolve to "true", the parsing + * will fail. + * + * @param cond condition expression + */ +#define VIMAP_PARSER_FAIL_UNLESS(cond) \ + if (!(cond)) \ + { \ + VIMAP_PARSER_FAIL(); \ + } + +/** Check for a token and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + */ +#define VIMAP_PARSER_CHECK(type) \ + VIMAP_PARSER_FAIL_UNLESS(parser.check <type>(line, &pos)) + +/** Check for an optional token and advance. + * If the token is not matched, parsing will continue anyway. + * + * @param type token class + */ +#define VIMAP_PARSER_TRY_CHECK(type) \ + (parser.check <type>(line, &pos)) + +/** Get a token and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + * @param variable variable which will receive pointer to the retrieved token + */ +#define VIMAP_PARSER_GET(type, variable) \ + { \ + variable.reset(parser.get <type>(line, &pos)); \ + VIMAP_PARSER_FAIL_UNLESS(variable.get()); \ + } + +/** Get an optional token and advance. + * If the token is not matched, parsing will continue anyway. + */ +#define VIMAP_PARSER_TRY_GET(type, variable) \ + (variable.reset(parser.get <type>(line, &pos)), variable.get()) + +/** Get an optional token and advance. If found, token will be pushed back + * to a vector. If the token is not matched, stopInstr will be executed. + * + * @param type token class + * @param variable variable of type std::vector<> to which the retrieved + * token will be pushed + * @param stopInstr instruction to execute if token is not found + */ +#define VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(type, variable, stopInstr) \ + { \ + std::unique_ptr <type> v; \ + try \ + { \ + v.reset(parser.get <type>(line, &pos)); \ + if (!v) \ + { \ + stopInstr; \ + } \ + variable.push_back(std::move(v)); \ + } \ + catch (...) \ + { \ + throw; \ + } \ + } + +/** Get a token and advance. Token will be pushed back to a vector. + * If the token is not matched, parsing will fail. + * + * @param type token class + * @param variable variable of type std::vector<> to which the retrieved + * token will be pushed + */ +#define VIMAP_PARSER_GET_PUSHBACK(type, variable) \ + VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(type, variable, VIMAP_PARSER_FAIL()) + + +/** Check for a token which takes an argument and advance. + * If the token is not matched, parsing will fail. + * + * @param type token class + */ +#define VIMAP_PARSER_CHECK_WITHARG(type, arg) \ + VIMAP_PARSER_FAIL_UNLESS(parser.checkWithArg <type>(line, &pos, arg)) + +/** Check for an optional token which takes an argument and advance. + * If the token is not matched, parsing will continue anyway. + * + * @param type token class + */ +#define VIMAP_PARSER_TRY_CHECK_WITHARG(type, arg) \ + (parser.checkWithArg <type>(line, &pos, arg)) + + + +namespace vmime { +namespace net { +namespace imap { + + +#if DEBUG_RESPONSE + static int IMAPParserDebugResponse_level = 0; + static std::vector <string> IMAPParserDebugResponse_stack; + + class IMAPParserDebugResponse { + + public: + + IMAPParserDebugResponse( + const string& name, + string& line, + const size_t currentPos, + const bool &result + ) + : m_name(name), + m_line(line), + m_pos(currentPos), + m_result(result) { + + ++IMAPParserDebugResponse_level; + IMAPParserDebugResponse_stack.push_back(name); + + for (int i = 0 ; i < IMAPParserDebugResponse_level ; ++i) { + std::cout << " "; + } + + std::cout << "ENTER(" << m_name << "), pos=" << m_pos; + std::cout << std::endl; + + for (std::vector <string>::iterator it = IMAPParserDebugResponse_stack.begin() ; + it != IMAPParserDebugResponse_stack.end() ; ++it) { + + std::cout << "> " << *it << " "; + } + + std::cout << std::endl; + std::cout << string(m_line.begin() + (m_pos < 30 ? 0U : m_pos - 30), + m_line.begin() + std::min(m_line.length(), m_pos + 30)) << std::endl; + + for (size_t i = (m_pos < 30 ? m_pos : (m_pos - (m_pos - 30))) ; i != 0 ; --i) { + std::cout << " "; + } + + std::cout << "^" << std::endl; + } + + ~IMAPParserDebugResponse() { + + for (int i = 0 ; i < IMAPParserDebugResponse_level ; ++i) { + std::cout << " "; + } + + std::cout << "LEAVE(" << m_name << "), result="; + std::cout << (m_result ? "TRUE" : "FALSE") << ", pos=" << m_pos; + std::cout << std::endl; + + --IMAPParserDebugResponse_level; + IMAPParserDebugResponse_stack.pop_back(); + } + + private: + + const string m_name; + string m_line; + size_t m_pos; + const bool& m_result; + }; + + + #define DEBUG_ENTER_COMPONENT(x, result) \ + IMAPParserDebugResponse dbg(x, line, *currentPos, result) + + #define DEBUG_FOUND(x, y) \ + std::cout << "FOUND: " << x << ": " << y << std::endl; +#else + #define DEBUG_ENTER_COMPONENT(x, result) + #define DEBUG_FOUND(x, y) +#endif + + +class VMIME_EXPORT IMAPParser : public object { + +public: + + IMAPParser() + : m_progress(NULL), + m_strict(false), + m_literalHandler(NULL) { + + } + + ~IMAPParser() { + + for (auto it = m_pendingResponses.begin() ; it != m_pendingResponses.end() ; ++it) { + delete it->second; + } + } + + + /** Set the socket currently used by this parser to receive data + * from server. + * + * @param sok socket + */ + void setSocket(const shared_ptr <socket>& sok) { + + m_socket = sok; + } + + /** Set the timeout handler currently used by this parser. + * + * @param toh timeout handler + */ + void setTimeoutHandler(const shared_ptr <timeoutHandler>& toh) { + + m_timeoutHandler = toh; + } + + /** Set the tracer currently used by this parser. + * + * @param tr tracer + */ + void setTracer(const shared_ptr <tracer>& tr) { + + m_tracer = tr; + } + + /** Set whether we operate in strict mode (this may not work + * with some servers which are not fully standard-compliant). + * + * @param strict true to operate in strict mode, or false + * to operate in default, relaxed mode + */ + void setStrict(const bool strict) { + + m_strict = strict; + } + + /** Return true if the parser operates in strict mode, or + * false otherwise. + * + * @return true if we are in strict mode, false otherwise + */ + bool isStrict() const { + + return m_strict; + } + + + + // + // literalHandler : literal content handler + // + + class component; + + class literalHandler { + + public: + + virtual ~literalHandler() { } + + + // Abstract target class + class target { + + protected: + + target(utility::progressListener* progress) : m_progress(progress) {} + target(const target&) {} + + public: + + virtual ~target() { } + + + utility::progressListener* progressListener() { return (m_progress); } + + virtual void putData(const string& chunk) = 0; + + virtual size_t getBytesWritten() const = 0; + + private: + + utility::progressListener* m_progress; + }; + + + // Target: put in a string + class targetString : public target { + + public: + + targetString(utility::progressListener* progress, vmime::string& str) + : target(progress), m_string(str), m_bytesWritten(0) { } + + const vmime::string& string() const { return (m_string); } + vmime::string& string() { return (m_string); } + + + void putData(const vmime::string& chunk) { + + m_string += chunk; + m_bytesWritten += chunk.length(); + } + + size_t getBytesWritten() const { + + return m_bytesWritten; + } + + private: + + vmime::string& m_string; + size_t m_bytesWritten; + }; + + + // Target: redirect to an output stream + class targetStream : public target { + + public: + + targetStream( + utility::progressListener* progress, + utility::outputStream& stream + ) + : target(progress), + m_stream(stream), + m_bytesWritten(0) { + + } + + const utility::outputStream& stream() const { return (m_stream); } + utility::outputStream& stream() { return (m_stream); } + + + void putData(const string& chunk) { + + m_stream.write(chunk.data(), chunk.length()); + m_bytesWritten += chunk.length(); + } + + size_t getBytesWritten() const { + + return m_bytesWritten; + } + + private: + + utility::outputStream& m_stream; + size_t m_bytesWritten; + }; + + + // Called when the parser needs to know what to do with a literal + // . comp: the component in which we are at this moment + // . data: data specific to the component (may not be used) + // + // Returns : + // . == NULL to put the literal into the response + // . != NULL to redirect the literal to the specified target + + virtual shared_ptr <target> targetFor(const component& comp, const int data) = 0; + }; + + + // + // Base class for a terminal or a non-terminal + // + + class component { + + public: + + component() { } + virtual ~component() { } + + virtual const string getComponentName() const = 0; + + bool parse(IMAPParser& parser, string& line, size_t* currentPos) { + + bool res = false; + DEBUG_ENTER_COMPONENT(getComponentName(), res); + res = parseImpl(parser, line, currentPos); + return res; + } + + virtual bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) = 0; + + + const string makeResponseLine( + const string& comp, + const string& line, + const size_t pos + ) { + +#if DEBUG_RESPONSE + if (pos > line.length()) { + std::cout << "WARNING: component::makeResponseLine(): pos > line.length()" << std::endl; + } +#endif + + string result(line.substr(0, pos)); + result += "[^]"; // indicates current parser position + result += line.substr(pos, line.length()); + if (!comp.empty()) result += " [" + comp + "]"; + + return (result); + } + }; + + +#define COMPONENT_ALIAS(parent, name) \ + class name : public parent { \ + virtual const string getComponentName() const { return #name; } \ + public: \ + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { \ + return parent::parseImpl(parser, line, currentPos); \ + } \ + } + +#define DECLARE_COMPONENT(name) \ + class name : public component { \ + virtual const string getComponentName() const { return #name; } \ + public: + + + // + // Parse one character + // + + template <char C> + class one_char : public component { + + public: + + const string getComponentName() const { + + return string("one_char <") + C + ">"; + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + const size_t pos = *currentPos; + + if (pos < line.length() && line[pos] == C) { + *currentPos = pos + 1; + return true; + } else { + return false; + } + } + }; + + + // + // SPACE ::= <ASCII SP, space, 0x20> + // + + DECLARE_COMPONENT(SPACE) + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) { + ++pos; + } + + if (pos > *currentPos) { + *currentPos = pos; + return true; + } else { + return false; + } + } + }; + + + // + // CR ::= <ASCII CR, carriage return, 0x0D> + // LF ::= <ASCII LF, line feed, 0x0A> + // CRLF ::= CR LF + // + + DECLARE_COMPONENT(CRLF) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_TRY_CHECK(SPACE); + + if (pos + 1 < line.length() && + line[pos] == 0x0d && line[pos + 1] == 0x0a) { + + *currentPos = pos + 2; + return true; + + } else { + + return false; + } + } + }; + + + // + // SPACE ::= <ASCII SP, space, 0x20> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // ATOM_CHAR ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // + // tag ::= 1*<any ATOM_CHAR except "+"> (named "xtag") + // + + DECLARE_COMPONENT(xtag) + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + bool end = false; + + tagString.reserve(10); + + while (!end && pos < line.length()) { + + const unsigned char c = line[pos]; + + switch (c) { + + case '+': + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) { + end = true; + } else { + tagString += c; + ++pos; + } + + break; + } + } + + *currentPos = pos; + return true; + } + + string tagString; + }; + + + // + // digit ::= "0" / digit_nz + // digit_nz ::= "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" + // + // number ::= 1*digit + // ;; Unsigned 32-bit integer + // ;; (0 <= n < 4,294,967,296) + // + + DECLARE_COMPONENT(number) + + number(const bool nonZero = false) + : value(0), + m_nonZero(nonZero) { + + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + bool valid = true; + unsigned int val = 0; + + while (valid && pos < line.length()) { + + const char c = line[pos]; + + if (c >= '0' && c <= '9') { + val = (val * 10) + (c - '0'); + ++pos; + } else { + valid = false; + } + } + + // Check for non-null length (and for non-zero number) + if (!(m_nonZero && val == 0) && pos != *currentPos) { + value = val; + *currentPos = pos; + return true; + } else { + return false; + } + } + + + unsigned long value; + + private: + + const bool m_nonZero; + }; + + + // nz_number ::= digit_nz *digit + // ;; Non-zero unsigned 32-bit integer + // ;; (0 < n < 4,294,967,296) + // + + class nz_number : public number { + + public: + + nz_number() + : number(true) { + + } + }; + + + // + // uniqueid ::= nz_number + // ;; Strictly ascending + // + + class uniqueid : public nz_number { + + public: + + uniqueid() + : nz_number() { + + } + }; + + + // uid-range = (uniqueid ":" uniqueid) + // ; two uniqueid values and all values + // ; between these two regards of order. + // ; Example: 2:4 and 4:2 are equivalent. + + DECLARE_COMPONENT(uid_range) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(uniqueid, uniqueid1); + VIMAP_PARSER_CHECK(one_char <':'>); + VIMAP_PARSER_GET(uniqueid, uniqueid2); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <uniqueid> uniqueid1; + std::unique_ptr <uniqueid> uniqueid2; + }; + + + // + // uid-set = (uniqueid / uid-range) *("," uid-set) + // + + DECLARE_COMPONENT(uid_set) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // We have either a 'uid_range' or a 'uniqueid' + if (!VIMAP_PARSER_TRY_GET(IMAPParser::uid_range, uid_range)) { + VIMAP_PARSER_GET(IMAPParser::uniqueid, uniqueid); + } + + // And maybe another 'uid-set' following + if (VIMAP_PARSER_TRY_CHECK(one_char <','>)) { + VIMAP_PARSER_GET(IMAPParser::uid_set, next_uid_set); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::uniqueid> uniqueid; + std::unique_ptr <IMAPParser::uid_range> uid_range; + + std::unique_ptr <IMAPParser::uid_set> next_uid_set; + }; + + + // + // text ::= 1*TEXT_CHAR + // + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // TEXT_CHAR ::= <any CHAR except CR and LF> + // + + DECLARE_COMPONENT(text) + + text(bool allow8bits = false, const char except = 0) + : m_allow8bits(allow8bits), + m_except(except) { + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t len = 0; + + if (m_allow8bits || !parser.isStrict()) { + + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) { + + const unsigned char c = line[pos]; + + if (c == 0x00 || c == 0x0d || c == 0x0a || c == except) { + end = true; + } else { + ++pos; + ++len; + } + } + + } else { + + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) { + + const unsigned char c = line[pos]; + + if (c < 0x01 || c > 0x7f || c == 0x0d || c == 0x0a || c == except) { + end = true; + } else { + ++pos; + ++len; + } + } + } + + if (len == 0) { + VIMAP_PARSER_FAIL(); + } + + value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, value.begin()); + + *currentPos = pos; + + return true; + } + + + string value; + + private: + + const bool m_allow8bits; + const char m_except; + }; + + + class text8 : public text { + + public: + + text8() : text(true) { + + } + }; + + + template <char C> + class text_except : public text { + + public: + + text_except() : text(false, C) { + + } + }; + + + template <char C> + class text8_except : public text { + + public: + + text8_except() : text(true, C) { + + } + }; + + + // + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + DECLARE_COMPONENT(QUOTED_CHAR) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + const unsigned char c = static_cast <unsigned char>(pos < line.length() ? line[pos] : 0); + + if (c >= 0x01 && c <= 0x7f && // 0x01 - 0x7f + c != '"' && c != '\\' && // quoted_specials + c != '\r' && c != '\n') { // CR and LF + + value = c; + *currentPos = pos + 1; + + } else if (c == '\\' && pos + 1 < line.length() && + (line[pos + 1] == '"' || line[pos + 1] == '\\')) { + + value = line[pos + 1]; + *currentPos = pos + 2; + + } else { + + VIMAP_PARSER_FAIL(); + } + + return true; + } + + + char value; + }; + + + // + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + DECLARE_COMPONENT(quoted_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t len = 0; + bool valid = false; + + value.reserve(line.length() - pos); + + for (bool end = false, quoted = false ; !end && pos < line.length() ; ) { + + const unsigned char c = line[pos]; + + if (quoted) { + + if (c == '"' || c == '\\') { + value += c; + } else { + value += '\\'; + value += c; + } + + quoted = false; + + ++pos; + ++len; + + } else { + + if (c == '\\') { + + quoted = true; + + ++pos; + ++len; + + } else if (c == '"') { + + valid = true; + end = true; + + } else if (c >= 0x01 && c <= 0x7f && // CHAR + c != 0x0a && c != 0x0d) { // CR and LF + + value += c; + + ++pos; + ++len; + + } else { + + valid = false; + end = true; + } + } + } + + if (!valid) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + + + string value; + }; + + + // + // nil ::= "NIL" + // + + DECLARE_COMPONENT(NIL) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "nil"); + + *currentPos = pos; + + return true; + } + }; + + + // + // string ::= quoted / literal ----> named 'xstring' + // + // nil ::= "NIL" + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // literal ::= "{" number "}" CRLF *CHAR8 + // ;; Number represents the number of CHAR8 octets + // CHAR8 ::= <any 8-bit octet except NUL, 0x01 - 0xff> + // + + DECLARE_COMPONENT(xstring) + + xstring( + const bool canBeNIL = false, + component* comp = NULL, + const int data = 0 + ) + : isNIL(true), + m_canBeNIL(canBeNIL), + m_component(comp), + m_data(data) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (m_canBeNIL && + VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "nil")) { + + // NIL + isNIL = true; + + } else { + + pos = *currentPos; + + isNIL = false; + + // quoted ::= <"> *QUOTED_CHAR <"> + if (VIMAP_PARSER_TRY_CHECK(one_char <'"'>)) { + + shared_ptr <quoted_text> text; + VIMAP_PARSER_GET(quoted_text, text); + VIMAP_PARSER_CHECK(one_char <'"'>); + + if (parser.m_literalHandler != NULL) { + + shared_ptr <literalHandler::target> target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) { + + value = "[literal-handler]"; + + const size_t length = text->value.length(); + utility::progressListener* progress = target->progressListener(); + + if (progress) { + progress->start(length); + } + + target->putData(text->value); + + if (progress) { + progress->progress(length, length); + progress->stop(length); + } + + } else { + + value = text->value; + } + + } else { + + value = text->value; + } + + DEBUG_FOUND("string[quoted]", "<length=" << value.length() << ", value='" << value << "'>"); + + // literal ::= "{" number "}" CRLF *CHAR8 + } else { + + VIMAP_PARSER_CHECK(one_char <'{'>); + + shared_ptr <number> num; + VIMAP_PARSER_GET(number, num); + + const size_t length = num->value; + + VIMAP_PARSER_CHECK(one_char <'}'> ); + + VIMAP_PARSER_CHECK(CRLF); + + + if (parser.m_literalHandler != NULL) { + + shared_ptr <literalHandler::target> target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) { + + value = "[literal-handler]"; + + parser.m_progress = target->progressListener(); + parser.readLiteral(*target, length); + parser.m_progress = NULL; + + } else { + + literalHandler::targetString target(NULL, value); + parser.readLiteral(target, length); + } + + } else { + + literalHandler::targetString target(NULL, value); + parser.readLiteral(target, length); + } + + line += parser.readLine(); + + DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << value << "'>"); + } + } + + *currentPos = pos; + + return true; + } + + + bool isNIL; + string value; + + private: + + bool m_canBeNIL; + + component* m_component; + const int m_data; + }; + + + // + // nstring ::= string / nil + // + + class nstring : public xstring { + + public: + + const string getComponentName() const { + + return "nstring"; + } + + nstring(component* comp = NULL, const int data = 0) + : xstring(true, comp, data) { + + } + }; + + + // + // astring ::= atom / string + // + + DECLARE_COMPONENT(astring) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <xstring> str; + VIMAP_PARSER_TRY_GET(xstring, str); + + if (str) { + value = str->value; + } else { + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + value = at->value; + } + + *currentPos = pos; + + return true; + } + + + string value; + }; + + + // + // atom ::= 1*ATOM_CHAR + // + // ATOM_CHAR ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // SPACE ::= <ASCII SP, space, 0x20> + // + + DECLARE_COMPONENT(atom) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + size_t len = 0; + + for (bool end = false ; !end && pos < line.length() ; ) { + + const unsigned char c = line[pos]; + + switch (c) { + + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + case '[': + case ']': // for "special_atom" + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) { + end = true; + } else { + ++pos; + ++len; + } + } + } + + if (len != 0) { + + value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, value.begin()); + + *currentPos = pos; + + } else { + + VIMAP_PARSER_FAIL(); + } + + return true; + } + + + string value; + }; + + + // + // special atom (eg. "CAPABILITY", "FLAGS", "STATUS"...) + // + // " Except as noted otherwise, all alphabetic characters are case- + // insensitive. The use of upper or lower case characters to define + // token strings is for editorial clarity only. Implementations MUST + // accept these strings in a case-insensitive fashion. " + // + + class special_atom : public atom { + + public: + + const std::string getComponentName() const { + + return string("special_atom <") + m_string + ">"; + } + + special_atom(const char* str) + : m_string(str) { // 'string' must be in lower-case + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!atom::parseImpl(parser, line, &pos)) { + return false; + } + + const char* cmp = value.c_str(); + const char* with = m_string; + + bool ok = true; + + while (ok && *cmp && *with) { + + ok = (std::tolower(*cmp, std::locale()) == *with); + + ++cmp; + ++with; + } + + if (!ok || *cmp || *with) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + + private: + + const char* m_string; + }; + + + // + // text_mime2 ::= "=?" <charset> "?" <encoding> "?" <encoded-text> "?=" + // ;; Syntax defined in [MIME-HDRS] + // + + DECLARE_COMPONENT(text_mime2) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <atom> theCharset, theEncoding; + std::unique_ptr <text> theText; + + VIMAP_PARSER_CHECK(one_char <'='> ); + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(atom, theCharset); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(atom, theEncoding); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + + VIMAP_PARSER_GET(text8_except <'?'> , theText); + + VIMAP_PARSER_CHECK(one_char <'?'> ); + VIMAP_PARSER_CHECK(one_char <'='> ); + + charset = theCharset->value; + + // Decode text + scoped_ptr <utility::encoder::encoder> theEncoder; + + if (theEncoding->value[0] == 'q' || theEncoding->value[0] == 'Q') { + + // Quoted-printable + theEncoder.reset(new utility::encoder::qpEncoder()); + theEncoder->getProperties()["rfc2047"] = true; + + } else if (theEncoding->value[0] == 'b' || theEncoding->value[0] == 'B') { + + // Base64 + theEncoder.reset(new utility::encoder::b64Encoder()); + } + + if (theEncoder.get()) { + + utility::inputStreamStringAdapter in(theText->value); + utility::outputStreamStringAdapter out(value); + + theEncoder->decode(in, out); + + // No decoder available + } else { + + value = theText->value; + } + + *currentPos = pos; + + return true; + } + + + vmime::charset charset; + string value; + }; + + + // seq-number = nz-number / "*" + // ; message sequence number (COPY, FETCH, STORE + // ; commands) or unique identifier (UID COPY, + // ; UID FETCH, UID STORE commands). + + DECLARE_COMPONENT(seq_number) + + seq_number() + : star(false) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + + star = true; + number.reset(); + + } else { + + star = false; + VIMAP_PARSER_GET(IMAPParser::number, number); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::number> number; + bool star; + }; + + + // seq-range = seq-number ":" seq-number + // ; two seq-number values and all values between + // ; these two regardless of order. + // ; Example: 2:4 and 4:2 are equivalent and indicate + // ; values 2, 3, and 4. + + DECLARE_COMPONENT(seq_range) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(seq_number, first); + + VIMAP_PARSER_CHECK(one_char <'*'> ); + + VIMAP_PARSER_GET(seq_number, last); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::seq_number> first; + std::unique_ptr <IMAPParser::seq_number> last; + }; + + + // sequence-set = (seq-number / seq-range) *("," sequence-set) + // ; set of seq-number values, regardless of order. + // ; Servers MAY coalesce overlaps and/or execute the + // ; sequence in any order. + // ; Example: a message sequence number set of + // ; 2,4:7,9,12:* for a mailbox with 15 messages is + // ; equivalent to 2,4,5,6,7,9,12,13,14,15 + + DECLARE_COMPONENT(sequence_set) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::seq_range, range)) { + VIMAP_PARSER_GET(IMAPParser::seq_number, number); + } + + if (VIMAP_PARSER_TRY_CHECK(one_char <','> )) { + VIMAP_PARSER_GET(sequence_set, nextSet); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::seq_number> number; + std::unique_ptr <IMAPParser::seq_range> range; + std::unique_ptr <IMAPParser::sequence_set> nextSet; + }; + + + // mod-sequence-value = 1*DIGIT + // ;; Positive unsigned 64-bit integer + // ;; (mod-sequence) + // ;; (1 <= n < 18,446,744,073,709,551,615) + + DECLARE_COMPONENT(mod_sequence_value) + + mod_sequence_value() + : value(0) { + + } + + bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + bool valid = true; + vmime_uint64 val = 0; + + while (valid && pos < line.length()) { + + const char c = line[pos]; + + if (c >= '0' && c <= '9') { + val = (val * 10) + (c - '0'); + ++pos; + } else { + valid = false; + } + } + + value = val; + + *currentPos = pos; + + return true; + } + + + vmime_uint64 value; + }; + + + // + // flag ::= "\Answered" / "\Flagged" / "\Deleted" / + // "\Seen" / "\Draft" / flag_keyword / flag_extension + // + // flag_extension ::= "\" atom + // ;; Future expansion. Client implementations + // ;; MUST accept flag_extension flags. Server + // ;; implementations MUST NOT generate + // ;; flag_extension flags except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + // flag_keyword ::= atom + // + + DECLARE_COMPONENT(flag) + + flag() + : type(UNKNOWN) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'\\'> )) { + + if (VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + + type = STAR; + + } else { + + shared_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + const string tname = utility::stringUtils::toLower(at->value); + + if (tname == "answered") { + type = ANSWERED; + } else if (tname == "flagged") { + type = FLAGGED; + } else if (tname == "deleted") { + type = DELETED; + } else if (tname == "seen") { + type = SEEN; + } else if (tname == "draft") { + type = DRAFT; + } else { + type = UNKNOWN; + name = tname; + } + } + + } else { + + if (!parser.isStrict() && VIMAP_PARSER_TRY_CHECK(one_char <'*'> )) { + type = STAR; // map "*" to "\*" + } else { + type = KEYWORD_OR_EXTENSION; + VIMAP_PARSER_GET(atom, flag_keyword); + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + UNKNOWN, + ANSWERED, + FLAGGED, + DELETED, + SEEN, + DRAFT, + KEYWORD_OR_EXTENSION, + STAR // * = custom flags allowed + }; + + + Type type; + string name; + + std::unique_ptr <IMAPParser::atom> flag_keyword; + }; + + + // + // flag_list ::= "(" #flag ")" + // + + DECLARE_COMPONENT(flag_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(flag, flags); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <flag>> flags; + }; + + + // + // mailbox ::= "INBOX" / astring + // ;; INBOX is case-insensitive. All case variants of + // ;; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX + // ;; not as an astring. Refer to section 5.1 for + // ;; further semantic details of mailbox names. + // + + DECLARE_COMPONENT(mailbox) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "inbox")) { + + type = INBOX; + name = "INBOX"; + + } else { + + type = OTHER; + + std::unique_ptr <astring> astr; + VIMAP_PARSER_GET(astring, astr); + name = astr->value; + } + + *currentPos = pos; + + return true; + } + + + enum Type { + INBOX, + OTHER + }; + + + Type type; + string name; + }; + + + // + // mailbox_flag := "\Marked" / "\Noinferiors" / + // "\Noselect" / "\Unmarked" / flag_extension + // + + DECLARE_COMPONENT(mailbox_flag) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'\\'> )) { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + const string tname = utility::stringUtils::toLower(at->value); + + type = UNKNOWN; // default + + switch (tname[0]) { + + case 'a': + + if (tname == "all") { + type = SPECIALUSE_ALL; + } else if (tname == "archive") { + type = SPECIALUSE_ARCHIVE; + } + + break; + + case 'd': + + if (tname == "drafts") { + type = SPECIALUSE_DRAFTS; + } + + break; + + case 'f': + + if (tname == "flagged") { + type = SPECIALUSE_FLAGGED; + } + + break; + + case 'h': + + if (tname == "haschildren") { + type = HASCHILDREN; + } else if (tname == "hasnochildren") { + type = HASNOCHILDREN; + } + + break; + + case 'i': + + if (tname == "important") { + type = SPECIALUSE_IMPORTANT; + } + + break; + + case 'j': + + if (tname == "junk") { + type = SPECIALUSE_JUNK; + } + + break; + + case 'm': + + if (tname == "marked") { + type = MARKED; + } + + break; + + case 'n': + + if (tname == "noinferiors") { + type = NOINFERIORS; + } else if (tname == "noselect") { + type = NOSELECT; + } + + break; + + case 's': + + if (tname == "sent") { + type = SPECIALUSE_SENT; + } + + break; + + case 't': + + if (tname == "trash") { + type = SPECIALUSE_TRASH; + } + + break; + + case 'u': + + if (tname == "unmarked") { + type = UNMARKED; + } + + break; + } + + if (type == UNKNOWN) { + name = "\\" + tname; + } + + } else { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + const string tname = utility::stringUtils::toLower(at->value); + + type = UNKNOWN; + name = tname; + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // RFC-3348 - Child Mailbox Extension + HASCHILDREN, + HASNOCHILDREN, + + // RFC-6154 - Special-Use Mailboxes + SPECIALUSE_ALL, + SPECIALUSE_ARCHIVE, + SPECIALUSE_DRAFTS, + SPECIALUSE_FLAGGED, + SPECIALUSE_JUNK, + SPECIALUSE_SENT, + SPECIALUSE_TRASH, + SPECIALUSE_IMPORTANT, // draft + + // Standard mailbox flags + UNKNOWN, + MARKED, + NOINFERIORS, + NOSELECT, + UNMARKED + }; + + + Type type; + string name; + }; + + + // + // mailbox_flag_list ::= "(" #(mailbox_flag) ")" + // + + DECLARE_COMPONENT(mailbox_flag_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(mailbox_flag, flags); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <mailbox_flag>> flags; + }; + + + // + // mailbox_list ::= mailbox_flag_list SPACE + // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + // + + DECLARE_COMPONENT(mailbox_list) + + mailbox_list() + : quoted_char('\0') { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::mailbox_flag_list, mailbox_flag_list); + + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_CHECK(NIL)) { + + VIMAP_PARSER_CHECK(one_char <'"'> ); + + std::unique_ptr <QUOTED_CHAR> qc; + VIMAP_PARSER_GET(QUOTED_CHAR, qc); + quoted_char = qc->value; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox, mailbox); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::mailbox_flag_list> mailbox_flag_list; + std::unique_ptr <IMAPParser::mailbox> mailbox; + + char quoted_char; + }; + + + // + // auth_type ::= atom + // ;; Defined by [IMAP-AUTH] + // + + DECLARE_COMPONENT(auth_type) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + name = utility::stringUtils::toLower(at->value); + + if (name == "kerberos_v4") { + type = KERBEROS_V4; + } else if (name == "gssapi") { + type = GSSAPI; + } else if (name == "skey") { + type = SKEY; + } else { + type = UNKNOWN; + } + + return true; + } + + + enum Type { + UNKNOWN, + + // RFC 1731 - IMAP4 Authentication Mechanisms + KERBEROS_V4, + GSSAPI, + SKEY + }; + + + Type type; + string name; + }; + + + // + // status-att-val = ("MESSAGES" SP number) / + // ("RECENT" SP number) / + // ("UIDNEXT" SP nz-number) / + // ("UIDVALIDITY" SP nz-number) / + // ("UNSEEN" SP number) + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer + // ;; extends non-terminal defined in [IMAPABNF]. + // ;; Value 0 denotes that the mailbox doesn't + // ;; support persistent mod-sequences + // + + DECLARE_COMPONENT(status_att_val) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "HIGHESTMODSEQ" SP mod-sequence-valzer + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "highestmodseq")) { + + type = HIGHESTMODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, value); + + } else { + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "messages")) { + type = MESSAGES; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "recent")) { + type = RECENT; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnext")) { + type = UIDNEXT; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidvalidity")) { + type = UIDVALIDITY; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "unseen"); + type = UNSEEN; + } + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::number, value); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // Extensions + HIGHESTMODSEQ, + + // Standard IMAP + MESSAGES, + RECENT, + UIDNEXT, + UIDVALIDITY, + UNSEEN + }; + + + Type type; + std::unique_ptr <IMAPParser::component> value; + + + const IMAPParser::number* value_as_number() const { + return dynamic_cast <IMAPParser::number *>(value.get()); + } + + const IMAPParser::mod_sequence_value* value_as_mod_sequence_value() const { + return dynamic_cast <IMAPParser::mod_sequence_value *>(value.get()); + } + }; + + + // status-att-list = status-att-val *(SP status-att-val) + + DECLARE_COMPONENT(status_att_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET_PUSHBACK(IMAPParser::status_att_val, values); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(IMAPParser::status_att_val, values); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <status_att_val>> values; + }; + + + // + // capability ::= "AUTH=" auth_type / atom + // ;; New capabilities MUST begin with "X" or be + // ;; registered with IANA as standard or standards-track + // + + DECLARE_COMPONENT(capability) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::atom, atom); + + string value = atom->value; + const char* str = value.c_str(); + + if ((str[0] == 'a' || str[0] == 'A') && + (str[1] == 'u' || str[1] == 'U') && + (str[2] == 't' || str[2] == 'T') && + (str[3] == 'h' || str[3] == 'H') && + (str[4] == '=')) { + + size_t pos = 5; + auth_type.reset(parser.get <IMAPParser::auth_type>(value, &pos)); + + atom.reset(); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::auth_type> auth_type; + std::unique_ptr <IMAPParser::atom> atom; + }; + + + // + // capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1" + // [SPACE 1#capability] + // ;; IMAP4rev1 servers which offer RFC 1730 + // ;; compatibility MUST list "IMAP4" as the first + // ;; capability. + // + + DECLARE_COMPONENT(capability_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "capability"); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + std::unique_ptr <capability> cap; + + if (parser.isStrict() || capabilities.empty()) { + VIMAP_PARSER_GET(capability, cap); + } else { + VIMAP_PARSER_TRY_GET(capability, cap); // allow SPACE at end of line (Apple iCloud IMAP server) + } + + if (!cap) { + break; + } + + capabilities.push_back(std::move(cap)); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <capability>> capabilities; + }; + + + // + // date_day_fixed ::= (SPACE digit) / 2digit + // ;; Fixed-format version of date_day + // + // date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + // + // date_year ::= 4digit + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich (that is, + // ;; (the amount that the given time differs from + // ;; Universal Time). Subtracting the timezone + // ;; from the given time will give the UT form. + // ;; The Universal Time zone is "+0000". + // + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + + DECLARE_COMPONENT(date_time) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // <"> date_day_fixed "-" date_month "-" date_year + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_TRY_CHECK(SPACE); + + shared_ptr <number> nd; + VIMAP_PARSER_GET(number, nd); + + VIMAP_PARSER_CHECK(one_char <'-'> ); + + shared_ptr <atom> amo; + VIMAP_PARSER_GET(atom, amo); + + VIMAP_PARSER_CHECK(one_char <'-'> ); + + shared_ptr <number> ny; + VIMAP_PARSER_GET(number, ny); + + VIMAP_PARSER_TRY_CHECK(SPACE); + + // 2digit ":" 2digit ":" 2digit + shared_ptr <number> nh; + VIMAP_PARSER_GET(number, nh); + + VIMAP_PARSER_CHECK(one_char <':'> ); + + shared_ptr <number> nmi; + VIMAP_PARSER_GET(number, nmi); + + VIMAP_PARSER_CHECK(one_char <':'> ); + + shared_ptr <number> ns; + VIMAP_PARSER_GET(number, ns); + + VIMAP_PARSER_TRY_CHECK(SPACE); + + // ("+" / "-") 4digit + int sign = 1; + + if (!(VIMAP_PARSER_TRY_CHECK(one_char <'+'> ))) { + VIMAP_PARSER_CHECK(one_char <'-'> ); + } + + shared_ptr <number> nz; + VIMAP_PARSER_GET(number, nz); + + VIMAP_PARSER_CHECK(one_char <'"'> ); + + + m_datetime.setHour(static_cast <int>(std::min(std::max(nh->value, 0ul), 23ul))); + m_datetime.setMinute(static_cast <int>(std::min(std::max(nmi->value, 0ul), 59ul))); + m_datetime.setSecond(static_cast <int>(std::min(std::max(ns->value, 0ul), 59ul))); + + const int zone = static_cast <int>(nz->value); + const int zh = zone / 100; // hour offset + const int zm = zone % 100; // minute offset + + m_datetime.setZone(((zh * 60) + zm) * sign); + + m_datetime.setDay(static_cast <int>(std::min(std::max(nd->value, 1ul), 31ul))); + m_datetime.setYear(static_cast <int>(ny->value)); + + const string month(utility::stringUtils::toLower(amo->value)); + int mon = vmime::datetime::JANUARY; + + if (month.length() >= 3) { + + switch (month[0]) { + + case 'j': { + + switch (month[1]) { + + case 'a': mon = vmime::datetime::JANUARY; break; + case 'u': { + + switch (month[2]) { + + case 'n': mon = vmime::datetime::JUNE; break; + default: mon = vmime::datetime::JULY; break; + } + + break; + } + + } + + break; + } + case 'f': mon = vmime::datetime::FEBRUARY; break; + case 'm': { + + switch (month[2]) { + case 'r': mon = vmime::datetime::MARCH; break; + default: mon = vmime::datetime::MAY; break; + } + + break; + } + case 'a': + { + switch (month[1]) { + case 'p': mon = vmime::datetime::APRIL; break; + default: mon = vmime::datetime::AUGUST; break; + } + + break; + } + case 's': mon = vmime::datetime::SEPTEMBER; break; + case 'o': mon = vmime::datetime::OCTOBER; break; + case 'n': mon = vmime::datetime::NOVEMBER; break; + case 'd': mon = vmime::datetime::DECEMBER; break; + } + } + + m_datetime.setMonth(mon); + + *currentPos = pos; + + return true; + } + + private: + + vmime::datetime m_datetime; + }; + + + // + // header_fld_name ::= astring + // + + COMPONENT_ALIAS(astring, header_fld_name); + + + // + // header_list ::= "(" 1#header_fld_name ")" + // + + DECLARE_COMPONENT(header_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(header_fld_name, fld_names); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <header_fld_name>> fld_names; + }; + + + // + // body_extension ::= nstring / number / "(" 1#body_extension ")" + // ;; Future expansion. Client implementations + // ;; MUST accept body_extension fields. Server + // ;; implementations MUST NOT generate + // ;; body_extension fields except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + + DECLARE_COMPONENT(body_extension) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + } else { + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::nstring, nstring)) { + VIMAP_PARSER_GET(IMAPParser::number, number); + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::nstring> nstring; + std::unique_ptr <IMAPParser::number> number; + + std::vector <std::unique_ptr <body_extension>> body_extensions; + }; + + + // + // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"] + // SPACE header_list / "TEXT" / "MIME" + // + + DECLARE_COMPONENT(section_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "HEADER.FIELDS" [".NOT"] SPACE header_list + const bool b1 = VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header.fields.not"); + const bool b2 = (b1 ? false : VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header.fields")); + + if (b1 || b2) { + + type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::header_list, header_list); + + // "HEADER" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "header")) { + + type = HEADER; + + // "MIME" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "mime")) { + + type = MIME; + + // "TEXT" + } else { + + type = TEXT; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "text"); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + HEADER, + HEADER_FIELDS, + HEADER_FIELDS_NOT, + MIME, + TEXT + }; + + + Type type; + std::unique_ptr <IMAPParser::header_list> header_list; + }; + + + // + // section ::= "[" [section_text / (nz_number *["." nz_number] + // ["." (section_text / "MIME")])] "]" + // + + DECLARE_COMPONENT(section) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'['> ); + + if (!VIMAP_PARSER_TRY_CHECK(one_char <']'> )) { + + if (!VIMAP_PARSER_TRY_GET(section_text, section_text1)) { + + std::unique_ptr <nz_number> num; + VIMAP_PARSER_GET(nz_number, num); + nz_numbers.push_back(static_cast <unsigned int>(num->value)); + + while (VIMAP_PARSER_TRY_CHECK(one_char <'.'> )) { + + if (VIMAP_PARSER_TRY_GET(nz_number, num)) { + nz_numbers.push_back(static_cast <unsigned int>(num->value)); + } else { + VIMAP_PARSER_GET(section_text, section_text2); + break; + } + } + } + + VIMAP_PARSER_CHECK(one_char <']'> ); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <section_text> section_text1; + std::unique_ptr <section_text> section_text2; + std::vector <unsigned int> nz_numbers; + }; + + + // + // addr_adl ::= nstring + // ;; Holds route from [RFC-822] route-addr if + // ;; non-NIL + // + // addr_host ::= nstring + // ;; NIL indicates [RFC-822] group syntax. + // ;; Otherwise, holds [RFC-822] domain name + // + // addr_mailbox ::= nstring + // ;; NIL indicates end of [RFC-822] group; if + // ;; non-NIL and addr_host is NIL, holds + // ;; [RFC-822] group name. + // ;; Otherwise, holds [RFC-822] local-part + // + // addr_name ::= nstring + // ;; Holds phrase from [RFC-822] mailbox if + // ;; non-NIL + // + // address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox + // SPACE addr_host ")" + // + + DECLARE_COMPONENT(address) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + VIMAP_PARSER_GET(nstring, addr_name); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_adl); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_mailbox); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(nstring, addr_host); + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <nstring> addr_name; + std::unique_ptr <nstring> addr_adl; + std::unique_ptr <nstring> addr_mailbox; + std::unique_ptr <nstring> addr_host; + }; + + + // + // address_list ::= "(" 1*address ")" / nil + // + + DECLARE_COMPONENT(address_list) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_CHECK(NIL)) { + + VIMAP_PARSER_CHECK(one_char <'('> ); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_GET_PUSHBACK(address, addresses); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <address>> addresses; + }; + + + // + // env_bcc ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_bcc); + + + // + // env_cc ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_cc); + + + // + // env_date ::= nstring + // + + COMPONENT_ALIAS(nstring, env_date); + + + // + // env_from ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_from); + + + // + // env_in_reply_to ::= nstring + // + + COMPONENT_ALIAS(nstring, env_in_reply_to); + + + // + // env_message_id ::= nstring + // + + COMPONENT_ALIAS(nstring, env_message_id); + + + // + // env_reply_to ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_reply_to); + + + // + // env_sender ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_sender); + + + // + // env_subject ::= nstring + // + + COMPONENT_ALIAS(nstring, env_subject); + + + // + // env_to ::= "(" 1*address ")" / nil + // + + COMPONENT_ALIAS(address_list, env_to); + + + // + // envelope ::= "(" env_date SPACE env_subject SPACE env_from + // SPACE env_sender SPACE env_reply_to SPACE env_to + // SPACE env_cc SPACE env_bcc SPACE env_in_reply_to + // SPACE env_message_id ")" + // + + DECLARE_COMPONENT(envelope) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + VIMAP_PARSER_GET(IMAPParser::env_date, env_date); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_subject, env_subject); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_from, env_from); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_sender, env_sender); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_reply_to, env_reply_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_to, env_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_cc, env_cc); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_bcc, env_bcc); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_in_reply_to, env_in_reply_to); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::env_message_id, env_message_id); + + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::env_date> env_date; + std::unique_ptr <IMAPParser::env_subject> env_subject; + std::unique_ptr <IMAPParser::env_from> env_from; + std::unique_ptr <IMAPParser::env_sender> env_sender; + std::unique_ptr <IMAPParser::env_reply_to> env_reply_to; + std::unique_ptr <IMAPParser::env_to> env_to; + std::unique_ptr <IMAPParser::env_cc> env_cc; + std::unique_ptr <IMAPParser::env_bcc> env_bcc; + std::unique_ptr <IMAPParser::env_in_reply_to> env_in_reply_to; + std::unique_ptr <IMAPParser::env_message_id> env_message_id; + }; + + + // + // body_fld_desc ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_desc); + + + // + // body_fld_id ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_id); + + + // + // body_fld_md5 ::= nstring + // + + COMPONENT_ALIAS(nstring, body_fld_md5); + + + // + // body_fld_octets ::= number + // + + COMPONENT_ALIAS(number, body_fld_octets); + + + // + // body_fld_lines ::= number + // + + COMPONENT_ALIAS(number, body_fld_lines); + + + // + // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ + // "QUOTED-PRINTABLE") <">) / string + // + + class body_fld_enc : public nstring { + + public: + + const string getComponentName() const { + + return "body_fld_enc"; + } + + body_fld_enc() { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!xstring::parseImpl(parser, line, &pos)) { + return false; + } + + // " When an IMAP4 client sends a FETCH (bodystructure) request + // to a server that is running the Exchange Server 2007 IMAP4 + // service, a corrupted response is sent as a reply " + // (see http://support.microsoft.com/kb/975918/en-us) + // + // Fail in strict mode + if (isNIL && parser.isStrict()) { + VIMAP_PARSER_FAIL(); + } + + *currentPos = pos; + + return true; + } + }; + + + // + // body_fld_param_item ::= string SPACE string + // + + DECLARE_COMPONENT(body_fld_param_item) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!parser.isStrict()) { + + // Some servers send an <atom> instead of a <string> here: + // eg. ... (CHARSET "X-UNKNOWN") ... + if (!VIMAP_PARSER_TRY_GET(xstring, string1)) { + + std::unique_ptr <atom> at; + VIMAP_PARSER_GET(atom, at); + + string1.reset(new xstring()); + string1->value = at->value; + } + + } else { + + VIMAP_PARSER_GET(xstring, string1); + } + + VIMAP_PARSER_CHECK(SPACE); + + if (!parser.isStrict()) { + + // In non-strict mode, allow NIL in value + std::unique_ptr <nstring> nstr; + VIMAP_PARSER_GET(nstring, nstr); + + string2.reset(new xstring()); + string2->value = nstr->value; + + } else { + + VIMAP_PARSER_GET(xstring, string2); + } + + DEBUG_FOUND("body_fld_param_item", "<" << string1->value << ", " << string2->value << ">"); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <xstring> string1; + std::unique_ptr <xstring> string2; + }; + + + // + // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil + // + + DECLARE_COMPONENT(body_fld_param) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + bool isNIL = false; + + if (!parser.isStrict()) { + + // In non-strict mode, allow "()" instead of "NIL" + if (VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + isNIL = true; + } + } + + if (!isNIL) { + + VIMAP_PARSER_GET_PUSHBACK(body_fld_param_item, items); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(body_fld_param_item, items); + } + } + + } else { + + VIMAP_PARSER_CHECK(NIL); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <body_fld_param_item>> items; + }; + + + // + // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil + // + + DECLARE_COMPONENT(body_fld_dsp) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET(xstring, m_string); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(class body_fld_param, m_body_fld_param); + VIMAP_PARSER_CHECK(one_char <')'> ); + + } else { + + VIMAP_PARSER_CHECK(NIL); + } + + *currentPos = pos; + + return true; + } + + private: + + std::unique_ptr <class xstring> m_string; + std::unique_ptr <class body_fld_param> m_body_fld_param; + + public: + + const class xstring* str() const { return m_string.get(); } + const class body_fld_param* body_fld_param() const { return m_body_fld_param.get(); } + }; + + + // + // body_fld_lang ::= nstring / "(" 1#string ")" + // + + DECLARE_COMPONENT(body_fld_lang) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'('> )) { + + VIMAP_PARSER_GET_PUSHBACK(xstring, strings); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(xstring, strings); + } + + } else { + + VIMAP_PARSER_GET_PUSHBACK(nstring, strings); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <xstring>> strings; + }; + + + // + // body_fields ::= body_fld_param SPACE body_fld_id SPACE + // body_fld_desc SPACE body_fld_enc SPACE + // body_fld_octets + // + + DECLARE_COMPONENT(body_fields) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_param, body_fld_param); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_id, body_fld_id); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_desc, body_fld_desc); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_enc, body_fld_enc); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_octets, body_fld_octets); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_param> body_fld_param; + std::unique_ptr <IMAPParser::body_fld_id> body_fld_id; + std::unique_ptr <IMAPParser::body_fld_desc> body_fld_desc; + std::unique_ptr <IMAPParser::body_fld_enc> body_fld_enc; + std::unique_ptr <IMAPParser::body_fld_octets> body_fld_octets; + }; + + + // + // media_subtype ::= string + // ;; Defined in [MIME-IMT] + // + + COMPONENT_ALIAS(xstring, media_subtype); + + + // + // media_text ::= <"> "TEXT" <"> SPACE media_subtype + // ;; Defined in [MIME-IMT] + // + + DECLARE_COMPONENT(media_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK_WITHARG(special_atom, "text"); + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + }; + + + // + // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <"> + // ;; Defined in [MIME-IMT] + // + + DECLARE_COMPONENT(media_message) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK_WITHARG(special_atom, "message"); + VIMAP_PARSER_CHECK(one_char <'"'> ); + VIMAP_PARSER_CHECK(SPACE); + + //VIMAP_PARSER_CHECK(one_char <'"'> ); + //VIMAP_PARSER_CHECK_WITHARG(special_atom, "rfc822"); + //VIMAP_PARSER_CHECK(one_char <'"'> ); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + }; + + + // + // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" / + // "MESSAGE" / "VIDEO") <">) / string) + // SPACE media_subtype + // ;; Defined in [MIME-IMT] + + DECLARE_COMPONENT(media_basic) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(xstring, media_type); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::xstring> media_type; + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + }; + + + // + // body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp + // [SPACE body_fld_lang + // [SPACE 1#body_extension]]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + // + + DECLARE_COMPONENT(body_ext_1part) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) + { + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_md5, body_fld_md5); + + // [SPACE body_fld_dsp + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_dsp, body_fld_dsp); + + // [SPACE body_fld_lang + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_lang, body_fld_lang); + + // [SPACE 1#body_extension] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + } + } + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_md5> body_fld_md5; + std::unique_ptr <IMAPParser::body_fld_dsp> body_fld_dsp; + std::unique_ptr <IMAPParser::body_fld_lang> body_fld_lang; + + std::vector <std::unique_ptr <body_extension>> body_extensions; + }; + + + // + // body_ext_mpart ::= body_fld_param + // [SPACE body_fld_dsp [SPACE body_fld_lang + // [SPACE 1#body_extension]]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + + DECLARE_COMPONENT(body_ext_mpart) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::body_fld_param, body_fld_param); + + // [SPACE body_fld_dsp [SPACE body_fld_lang [SPACE 1#body_extension]]] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_dsp, body_fld_dsp); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET(IMAPParser::body_fld_lang, body_fld_lang); + + // [SPACE 1#body_extension] + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(body_extension, body_extensions); + } + } + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_fld_param> body_fld_param; + std::unique_ptr <IMAPParser::body_fld_dsp> body_fld_dsp; + std::unique_ptr <IMAPParser::body_fld_lang> body_fld_lang; + + std::vector <std::unique_ptr <body_extension>> body_extensions; + }; + + + // + // body_type_basic ::= media_basic SPACE body_fields + // ;; MESSAGE subtype MUST NOT be "RFC822" + // + + DECLARE_COMPONENT(body_type_basic) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_basic, media_basic); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_basic> media_basic; + std::unique_ptr <IMAPParser::body_fields> body_fields; + }; + + + // + // body_type_msg ::= media_message SPACE body_fields SPACE envelope + // SPACE body SPACE body_fld_lines + // + + class xbody; + typedef xbody body; + + DECLARE_COMPONENT(body_type_msg) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_message, media_message); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + VIMAP_PARSER_CHECK(SPACE); + + // BUGFIX: made SPACE optional. This is not standard, but some servers + // seem to return responses like that... + VIMAP_PARSER_GET(IMAPParser::envelope, envelope); + VIMAP_PARSER_TRY_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::xbody, body); + VIMAP_PARSER_TRY_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_lines, body_fld_lines); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_message> media_message; + std::unique_ptr <IMAPParser::body_fields> body_fields; + std::unique_ptr <IMAPParser::envelope> envelope; + std::unique_ptr <IMAPParser::xbody> body; + std::unique_ptr <IMAPParser::body_fld_lines> body_fld_lines; + }; + + + // + // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines + // + + DECLARE_COMPONENT(body_type_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::media_text, media_text); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fields, body_fields); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::body_fld_lines, body_fld_lines); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_text> media_text; + std::unique_ptr <IMAPParser::body_fields> body_fields; + std::unique_ptr <IMAPParser::body_fld_lines> body_fld_lines; + }; + + + // + // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text) + // [SPACE body_ext_1part] + // + + DECLARE_COMPONENT(body_type_1part) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_text, body_type_text)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_msg, body_type_msg)) { + VIMAP_PARSER_GET(IMAPParser::body_type_basic, body_type_basic); + } + } + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_ext_1part, body_ext_1part)) { + --pos; + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_type_basic> body_type_basic; + std::unique_ptr <IMAPParser::body_type_msg> body_type_msg; + std::unique_ptr <IMAPParser::body_type_text> body_type_text; + + std::unique_ptr <IMAPParser::body_ext_1part> body_ext_1part; + }; + + + // + // body_type_mpart ::= 1*body SPACE media_subtype + // [SPACE body_ext_mpart] + // + + DECLARE_COMPONENT(body_type_mpart) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET_PUSHBACK(xbody, list); + + while (true) { + VIMAP_PARSER_TRY_GET_PUSHBACK_OR_ELSE(xbody, list, break); + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::media_subtype, media_subtype); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(IMAPParser::body_ext_mpart, body_ext_mpart); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::media_subtype> media_subtype; + std::unique_ptr <IMAPParser::body_ext_mpart> body_ext_mpart; + + std::vector <std::unique_ptr <xbody>> list; + }; + + + // + // xbody ::= "(" body_type_1part / body_type_mpart ")" + // + + DECLARE_COMPONENT(xbody) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::body_type_mpart, body_type_mpart)) { + VIMAP_PARSER_GET(IMAPParser::body_type_1part, body_type_1part); + } + + VIMAP_PARSER_CHECK(one_char <')'> ); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::body_type_1part> body_type_1part; + std::unique_ptr <IMAPParser::body_type_mpart> body_type_mpart; + }; + + + // msg_att_item ::= "ENVELOPE" SPACE envelope / + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" / + // "INTERNALDATE" SPACE date_time / + // "RFC822" [".HEADER" / ".TEXT"] SPACE nstring / + // "RFC822.SIZE" SPACE number / + // "BODY" ["STRUCTURE"] SPACE body / + // "BODY" section ["<" number ">"] SPACE nstring / + // "UID" SPACE uniqueid + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // msg_att_item /= "MODSEQ" SP "(" mod_sequence_value ")" + + DECLARE_COMPONENT(msg_att_item) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "ENVELOPE" SPACE envelope + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "envelope")) { + + type = ENVELOPE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::envelope, envelope); + + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "flags")) { + + type = FLAGS; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::flag_list, flag_list); + + // "INTERNALDATE" SPACE date_time + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "internaldate")) { + + type = INTERNALDATE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::date_time, date_time); + + // "RFC822" ".HEADER" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.header")) { + + type = RFC822_HEADER; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::nstring, nstring); + + // "RFC822" ".TEXT" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.text")) { + + type = RFC822_TEXT; + + VIMAP_PARSER_CHECK(SPACE); + + nstring.reset(parser.getWithArgs <IMAPParser::nstring>(line, &pos, this, RFC822_TEXT)); + + VIMAP_PARSER_FAIL_UNLESS(nstring); + + // "RFC822.SIZE" SPACE number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822.size")) { + + type = RFC822_SIZE; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::number, number); + + // "RFC822" SPACE nstring + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "rfc822")) { + + type = RFC822; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::nstring, nstring); + + // "BODY" "STRUCTURE" SPACE body + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "bodystructure")) { + + type = BODY_STRUCTURE; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::body, body); + + // "BODY" section ["<" number ">"] SPACE nstring + // "BODY" SPACE body + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "body")) { + + VIMAP_PARSER_TRY_GET(IMAPParser::section, section); + + // "BODY" section ["<" number ">"] SPACE nstring + if (section != NULL) { + + type = BODY_SECTION; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'<'> )) { + VIMAP_PARSER_GET(IMAPParser::number, number); + VIMAP_PARSER_CHECK(one_char <'>'> ); + } + + VIMAP_PARSER_CHECK(SPACE); + + nstring.reset(parser.getWithArgs <IMAPParser::nstring>(line, &pos, this, BODY_SECTION)); + + VIMAP_PARSER_FAIL_UNLESS(nstring); + + // "BODY" SPACE body + } else { + + type = BODY; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::body, body); + } + + // "MODSEQ" SP "(" mod_sequence_value ")" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "modseq")) { + + type = MODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_CHECK(one_char <'('> ); + + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, mod_sequence_value); + + VIMAP_PARSER_CHECK(one_char <')'> ); + + // "UID" SPACE uniqueid + } else { + + type = UID; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "uid"); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::uniqueid, uniqueid); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + ENVELOPE, + FLAGS, + INTERNALDATE, + RFC822, + RFC822_SIZE, + RFC822_HEADER, + RFC822_TEXT, + BODY, + BODY_SECTION, + BODY_STRUCTURE, + UID, + MODSEQ + }; + + + Type type; + + std::unique_ptr <IMAPParser::date_time> date_time; + std::unique_ptr <IMAPParser::number> number; + std::unique_ptr <IMAPParser::envelope> envelope; + std::unique_ptr <IMAPParser::uniqueid> uniqueid; + std::unique_ptr <IMAPParser::nstring> nstring; + std::unique_ptr <IMAPParser::xbody> body; + std::unique_ptr <IMAPParser::flag_list> flag_list; + std::unique_ptr <IMAPParser::section> section; + std::unique_ptr <IMAPParser::mod_sequence_value> mod_sequence_value; + }; + + + // + // msg_att ::= "(" 1#(msg_att_item) ")" + // + + DECLARE_COMPONENT(msg_att) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'('> ); + + items.push_back(std::move(std::unique_ptr <msg_att_item>(parser.get <msg_att_item>(line, &pos)))); + + while (!VIMAP_PARSER_TRY_CHECK(one_char <')'> )) { + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET_PUSHBACK(msg_att_item, items); + } + + *currentPos = pos; + + return true; + } + + + std::vector <std::unique_ptr <msg_att_item>> items; + }; + + + // + // message_data ::= nz_number SPACE ("EXPUNGE" / + // ("FETCH" SPACE msg_att)) + // + + DECLARE_COMPONENT(message_data) + + message_data() + : number(0) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + scoped_ptr <nz_number> num; + VIMAP_PARSER_GET(nz_number, num); + number = static_cast <unsigned int>(num->value); + + VIMAP_PARSER_CHECK(SPACE); + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "expunge")) { + + type = EXPUNGE; + + } else { + + type = FETCH; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "fetch"); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::msg_att, msg_att); + } + + *currentPos = pos; + + return true; + } + + + enum Type { + EXPUNGE, + FETCH + }; + + + Type type; + unsigned int number; + std::unique_ptr <IMAPParser::msg_att> msg_att; + }; + + + // + // resp_text_code ::= "ALERT" / "PARSE" / + // capability-data / + // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + // "UIDVALIDITY" SPACE nz_number / + // "UNSEEN" SPACE nz_number / + // "UIDNEXT" SPACE nz-number / + // atom [SPACE 1*<any TEXT_CHAR except "]">] + // + // IMAP Extension for Conditional STORE (RFC-4551): + // + // resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value / + // "NOMODSEQ" / + // "MODIFIED" SP set + // + // IMAP UIDPLUS Extension (RFC-4315): + // + // resp-text-code =/ "APPENDUID" SP nz-number SP append-uid / + // "COPYUID" SP nz-number SP uid-set SP uid-set / + // "UIDNOTSTICKY" + + DECLARE_COMPONENT(resp_text_code) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + // "ALERT" + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "alert")) { + + type = ALERT; + + // "PARSE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "parse")) { + + type = PARSE; + + // capability_data + } else if (VIMAP_PARSER_TRY_GET(IMAPParser::capability_data, capability_data)) { + + type = CAPABILITY; + + // "PERMANENTFLAGS" SPACE flag_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "permanentflags")) { + + type = PERMANENTFLAGS; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::flag_list, flag_list); + + // "READ-ONLY" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "read-only")) { + + type = READ_ONLY; + + // "READ-WRITE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "read-write")) { + + type = READ_WRITE; + + // "TRYCREATE" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "trycreate")) { + + type = TRYCREATE; + + // "UIDVALIDITY" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidvalidity")) { + + type = UIDVALIDITY; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "UIDNEXT" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnext")) { + + type = UIDNEXT; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "UNSEEN" SPACE nz_number + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "unseen")) { + + type = UNSEEN; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + + // "HIGHESTMODSEQ" SP mod-sequence-value + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "highestmodseq")) { + + type = HIGHESTMODSEQ; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::mod_sequence_value, mod_sequence_value); + + // "NOMODSEQ" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "nomodseq")) { + + type = NOMODSEQ; + + // "MODIFIED" SP sequence-set + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "modified")) { + + type = MODIFIED; + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::sequence_set, sequence_set); + + // "APPENDUID" SP nz-number SP append-uid + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "appenduid")) { + + type = APPENDUID; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set); + + // "COPYUID" SP nz-number SP uid-set SP uid-set + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "copyuid")) { + + type = COPYUID; + + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::nz_number, nz_number); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::uid_set, uid_set2); + + // "UIDNOTSTICKY" + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "uidnotsticky")) { + + type = UIDNOTSTICKY; + + // atom [SPACE 1*<any TEXT_CHAR except "]">] + } else { + + type = OTHER; + + VIMAP_PARSER_GET(IMAPParser::atom, atom); + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(text_except <']'> , text); + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + // Extensions + HIGHESTMODSEQ, + NOMODSEQ, + MODIFIED, + APPENDUID, + COPYUID, + UIDNOTSTICKY, + + // Standard IMAP + ALERT, + PARSE, + CAPABILITY, + PERMANENTFLAGS, + READ_ONLY, + READ_WRITE, + TRYCREATE, + UIDVALIDITY, + UIDNEXT, + UNSEEN, + OTHER + }; + + + Type type; + + std::unique_ptr <IMAPParser::nz_number> nz_number; + std::unique_ptr <IMAPParser::atom> atom; + std::unique_ptr <IMAPParser::flag_list> flag_list; + std::unique_ptr <IMAPParser::text> text; + std::unique_ptr <IMAPParser::mod_sequence_value> mod_sequence_value; + std::unique_ptr <IMAPParser::sequence_set> sequence_set; + std::unique_ptr <IMAPParser::capability_data> capability_data; + std::unique_ptr <IMAPParser::uid_set> uid_set; + std::unique_ptr <IMAPParser::uid_set> uid_set2; + }; + + + // + // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + // ;; text SHOULD NOT begin with "[" or "=" + + DECLARE_COMPONENT(resp_text) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK(one_char <'['> )) { + + VIMAP_PARSER_GET(IMAPParser::resp_text_code, resp_text_code); + + VIMAP_PARSER_CHECK(one_char <']'> ); + VIMAP_PARSER_TRY_CHECK(SPACE); + } + + std::unique_ptr <text_mime2> text1; + VIMAP_PARSER_TRY_GET(text_mime2, text1); + + if (text1.get()) { + + text = text1->value; + + } else { + + std::unique_ptr <IMAPParser::text> text2; + VIMAP_PARSER_TRY_GET(IMAPParser::text, text2); + + if (text2.get()) { + text = text2->value; + } else { + // Empty response text + } + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text_code> resp_text_code; + string text; + }; + + + // + // continue_req ::= "+" SPACE (resp_text / base64) + // + + DECLARE_COMPONENT(continue_req) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'+'> ); + + if (!parser.isStrict()) { + + // Some servers do not send SPACE when response text is empty + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + } else { + resp_text.reset(new IMAPParser::resp_text()); // empty + } + + } else { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + // ;; Status condition + // + + DECLARE_COMPONENT(resp_cond_state) + + resp_cond_state() + : status(BAD) { + + } + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "ok")) { + status = OK; + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "no")) { + status = NO; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "bad"); + status = BAD; + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + enum Status { + OK, + NO, + BAD + }; + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + Status status; + }; + + + // + // resp_cond_bye ::= "BYE" SPACE resp_text + // + + DECLARE_COMPONENT(resp_cond_bye) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "bye"); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text + // ;; Authentication condition + // + + DECLARE_COMPONENT(resp_cond_auth) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "ok")) { + condition = OK; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "preauth"); + condition = PREAUTH; + } + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_text, resp_text); + + *currentPos = pos; + + return true; + } + + + enum Condition { + OK, + PREAUTH + }; + + + Condition condition; + std::unique_ptr <IMAPParser::resp_text> resp_text; + }; + + + // + // mailbox_data ::= "FLAGS" SPACE mailbox_flag_list / + // "LIST" SPACE mailbox_list / + // "LSUB" SPACE mailbox_list / + // "MAILBOX" SPACE text / + // "SEARCH" [SPACE 1#nz_number] / + // "STATUS" SPACE mailbox SPACE + // "(" [status-att-list] ")" / + // number SPACE "EXISTS" / + // number SPACE "RECENT" + // + + DECLARE_COMPONENT(mailbox_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (VIMAP_PARSER_TRY_GET(IMAPParser::number, number)) { + + VIMAP_PARSER_CHECK(SPACE); + + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "exists")) { + type = EXISTS; + } else { + VIMAP_PARSER_CHECK_WITHARG(special_atom, "recent"); + type = RECENT; + } + + } else { + + // "FLAGS" SPACE mailbox_flag_list + if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "flags")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_flag_list, mailbox_flag_list); + + type = FLAGS; + + // "LIST" SPACE mailbox_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "list")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_list, mailbox_list); + + type = LIST; + + // "LSUB" SPACE mailbox_list + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "lsub")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox_list, mailbox_list); + + type = LSUB; + + // "MAILBOX" SPACE text + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "mailbox")) { + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::text, text); + + type = MAILBOX; + + // "SEARCH" [SPACE 1#nz_number] + } else if (VIMAP_PARSER_TRY_CHECK_WITHARG(special_atom, "search")) { + + if (VIMAP_PARSER_TRY_CHECK(SPACE)) { + + VIMAP_PARSER_GET_PUSHBACK(nz_number, search_nz_number_list); + + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + VIMAP_PARSER_GET_PUSHBACK(nz_number, search_nz_number_list); + } + } + + type = SEARCH; + + // "STATUS" SPACE mailbox SPACE + // "(" [status_att_list] ")" + } else { + + VIMAP_PARSER_CHECK_WITHARG(special_atom, "status"); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::mailbox, mailbox); + + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_CHECK(one_char <'('> ); + VIMAP_PARSER_TRY_GET(IMAPParser::status_att_list, status_att_list); + VIMAP_PARSER_CHECK(one_char <')'> ); + + type = STATUS; + } + } + + *currentPos = pos; + + return true; + } + + + enum Type { + + FLAGS, + LIST, + LSUB, + MAILBOX, + SEARCH, + STATUS, + EXISTS, + RECENT + }; + + + Type type; + + std::unique_ptr <IMAPParser::number> number; + std::unique_ptr <IMAPParser::mailbox_flag_list> mailbox_flag_list; + std::unique_ptr <IMAPParser::mailbox_list> mailbox_list; + std::unique_ptr <IMAPParser::mailbox> mailbox; + std::unique_ptr <IMAPParser::text> text; + std::vector <std::unique_ptr <nz_number>> search_nz_number_list; + std::unique_ptr <IMAPParser::status_att_list> status_att_list; + }; + + + // + // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + // mailbox_data / message_data / capability_data) CRLF + // + + DECLARE_COMPONENT(response_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_state, resp_cond_state)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_bye, resp_cond_bye)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::mailbox_data, mailbox_data)) { + if (!VIMAP_PARSER_TRY_GET(IMAPParser::message_data, message_data)) { + VIMAP_PARSER_GET(IMAPParser::capability_data, capability_data); + } + } + } + } + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_cond_state> resp_cond_state; + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + std::unique_ptr <IMAPParser::mailbox_data> mailbox_data; + std::unique_ptr <IMAPParser::message_data> message_data; + std::unique_ptr <IMAPParser::capability_data> capability_data; + }; + + + DECLARE_COMPONENT(continue_req_or_response_data) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::continue_req, continue_req)) { + VIMAP_PARSER_GET(IMAPParser::response_data, response_data); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::continue_req> continue_req; + std::unique_ptr <IMAPParser::response_data> response_data; + }; + + + // + // response_fatal ::= "*" SPACE resp_cond_bye CRLF + // ;; Server closes connection immediately + // + + DECLARE_COMPONENT(response_fatal) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + VIMAP_PARSER_GET(IMAPParser::resp_cond_bye, resp_cond_bye); + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + }; + + + // + // response_tagged ::= tag SPACE resp_cond_state CRLF + // + + DECLARE_COMPONENT(response_tagged) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_GET(IMAPParser::xtag, tag); + VIMAP_PARSER_CHECK(SPACE); + VIMAP_PARSER_GET(IMAPParser::resp_cond_state, resp_cond_state); + + if (!parser.isStrict()) { + + // Allow SPACEs at end of line + while (VIMAP_PARSER_TRY_CHECK(SPACE)) { + ; + } + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::xtag> tag; + std::unique_ptr <IMAPParser::resp_cond_state> resp_cond_state; + }; + + + // + // response_done ::= response_tagged / response_fatal + // + + DECLARE_COMPONENT(response_done) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::response_tagged, response_tagged)) { + VIMAP_PARSER_GET(IMAPParser::response_fatal, response_fatal); + } + + *currentPos = pos; + + return true; + } + + + std::unique_ptr <IMAPParser::response_tagged> response_tagged; + std::unique_ptr <IMAPParser::response_fatal> response_fatal; + }; + + + // + // response ::= *(continue_req / response_data) response_done + // + + DECLARE_COMPONENT(response) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + string curLine = line; + bool partial = false; // partial response + + IMAPParser::continue_req_or_response_data* resp = NULL; + + while ((resp = parser.get <IMAPParser::continue_req_or_response_data>(curLine, &pos))) { + + continue_req_or_response_data.push_back( + std::move( + std::unique_ptr <IMAPParser::continue_req_or_response_data>(resp) + ) + ); + + // Partial response (continue_req) + if (resp->continue_req) { + partial = true; + break; + } + + // We have read a CRLF, read another line + curLine = parser.readLine(); + pos = 0; + } + + if (!partial) { + response_done.reset(parser.get <IMAPParser::response_done>(curLine, &pos)); + VIMAP_PARSER_FAIL_UNLESS(response_done); + } + + *currentPos = pos; + + return true; + } + + + bool isBad() const { + + if (!response_done) { // incomplete (partial) response + return true; + } + + if (response_done->response_fatal) { + return true; + } + + if (response_done->response_tagged->resp_cond_state->status == IMAPParser::resp_cond_state::BAD) { + + return true; + } + + return false; + } + + void setErrorLog(const string& errorLog) { + + m_errorLog = errorLog; + } + + const string& getErrorLog() const { + + return m_errorLog; + } + + + std::vector <std::unique_ptr <IMAPParser::continue_req_or_response_data>> continue_req_or_response_data; + std::unique_ptr <IMAPParser::response_done> response_done; + + private: + + string m_errorLog; + }; + + + // + // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF + // + + DECLARE_COMPONENT(greeting) + + bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { + + size_t pos = *currentPos; + + VIMAP_PARSER_CHECK(one_char <'*'> ); + VIMAP_PARSER_CHECK(SPACE); + + if (!VIMAP_PARSER_TRY_GET(IMAPParser::resp_cond_auth, resp_cond_auth)) { + VIMAP_PARSER_GET(IMAPParser::resp_cond_bye, resp_cond_bye); + } + + VIMAP_PARSER_CHECK(CRLF); + + *currentPos = pos; + + return true; + } + + void setErrorLog(const string& errorLog) { + + m_errorLog = errorLog; + } + + const string& getErrorLog() const { + + return m_errorLog; + } + + + std::unique_ptr <IMAPParser::resp_cond_auth> resp_cond_auth; + std::unique_ptr <IMAPParser::resp_cond_bye> resp_cond_bye; + + private: + + string m_errorLog; + }; + + + + // + // The main functions used to parse a response + // + + response* readResponse(const IMAPTag& tag, literalHandler* lh = NULL) { + + while (true) { + + auto it = m_pendingResponses.find(std::string(tag)); + + if (it != m_pendingResponses.end()) { + auto* resp = it->second; + m_pendingResponses.erase(it); + return resp; + } + + size_t pos = 0; + string line = readLine(); + + m_literalHandler = lh; + response* resp = get <response>(line, &pos); + m_literalHandler = NULL; + + if (!resp) { + throw exceptions::invalid_response("", m_errorResponseLine); + } + + resp->setErrorLog(lastLine()); + + // If there is a continue_req, return the response immediately + for (auto &respData : resp->continue_req_or_response_data) { + if (respData->continue_req) { + return resp; + } + } + + // Else, return response if the tag is the one we expect + if (resp->response_done && resp->response_done->response_tagged && + resp->response_done->response_tagged->tag) { + + if (tag == resp->response_done->response_tagged->tag->tagString) { + return resp; + } else { + // Not our response tag, cache it for later + m_pendingResponses[resp->response_done->response_tagged->tag->tagString] = resp; + } + } else { + // Untagged response + return resp; + } + } + } + + + greeting* readGreeting() { + + size_t pos = 0; + string line = readLine(); + + greeting* greet = get <greeting>(line, &pos); + + if (!greet) { + throw exceptions::invalid_response("", m_errorResponseLine); + } + + greet->setErrorLog(lastLine()); + + return greet; + } + + + /** Parse a token and advance. + * If the token has been parsed successfully, a raw pointer to it + * will be returned. The caller is responsible to free the memory. + * + * @param TYPE token type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @return a raw pointer to the parsed token, or NULL otherwise + */ + template <class TYPE> + TYPE* get(string& line, size_t* currentPos) { + + component* resp = new TYPE; + return internalGet <TYPE>(resp, line, currentPos); + } + + /** Parse a token which takes 2 arguments and advance. + * If the token has been parsed successfully, a raw pointer to it + * will be returned. The caller is responsible to free the memory. + * + * @param TYPE token type (class) + * @param ARG1_TYPE type of argument #1 (class) + * @param ARG2_TYPE type of argument #2 (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @param arg1 value of argument 1 to be passed to the token + * @param arg2 value of argument 2 to be passed to the token + * @return a raw pointer to the parsed token, or NULL otherwise + */ + template <class TYPE, class ARG1_TYPE, class ARG2_TYPE> + TYPE* getWithArgs(string& line, size_t* currentPos, ARG1_TYPE arg1, ARG2_TYPE arg2) { + + component* resp = new TYPE(arg1, arg2); + return internalGet <TYPE>(resp, line, currentPos); + } + +private: + + template <class TYPE> + TYPE* internalGet(component* resp, string& line, size_t* currentPos) { + + const size_t oldPos = *currentPos; + + if (!resp->parse(*this, line, currentPos)) { + + *currentPos = oldPos; + + delete resp; + + return NULL; + } + + return static_cast <TYPE*>(resp); + } + + const string lastLine() const { + + // Remove blanks and new lines at the end of the line. + string line(m_lastLine); + + string::const_iterator it = line.end(); + int count = 0; + + while (it != line.begin()) { + + const unsigned char c = *(it - 1); + + if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) { + break; + } + + ++count; + --it; + } + + line.resize(line.length() - count); + + return (line); + } + +public: + + /** Check for a token and advance. + * + * @param TYPE token type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @return true if the token has been parsed, or false otherwise + */ + template <class TYPE> + bool check(string& line, size_t* currentPos) { + + const size_t oldPos = *currentPos; + + TYPE term; + + if (!term.parse(*this, line, currentPos)) { + *currentPos = oldPos; + return false; + } else { + return true; + } + } + + /** Check for a token which takes an argument and advance. + * + * @param TYPE token type (class) + * @param ARG_TYPE argument type (class) + * @param line line which is currently being parsed + * @param currentPos current position in the line (will be updated + * when the function returns) + * @param arg argument to be passed to the token + * @return true if the token has been parsed, or false otherwise + */ + template <class TYPE, class ARG_TYPE> + bool checkWithArg(string& line, size_t* currentPos, const ARG_TYPE arg) { + + const size_t oldPos = *currentPos; + + TYPE term(arg); + + if (!term.parse(*this, line, currentPos)) { + *currentPos = oldPos; + return false; + } else { + return true; + } + } + + +private: + + weak_ptr <socket> m_socket; + shared_ptr <tracer> m_tracer; + + utility::progressListener* m_progress; + + bool m_strict; + + literalHandler* m_literalHandler; + + weak_ptr <timeoutHandler> m_timeoutHandler; + + + string m_buffer; + + string m_lastLine; + string m_errorResponseLine; + + std::map <std::string, response*> m_pendingResponses; + +public: + + /** Read a line from the input buffer. The function blocks until a + * complete line is read from the buffer. Position in input buffer + * will be updated. + * + * @return next line + */ + const string readLine() { + + size_t pos; + + while ((pos = m_buffer.find('\n')) == string::npos) { + read(); + } + + string line; + line.resize(pos + 1); + std::copy(m_buffer.begin(), m_buffer.begin() + pos + 1, line.begin()); + + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + pos + 1); + + m_lastLine = line; + +#if DEBUG_RESPONSE + std::cout << std::endl << "Read line:" << std::endl << line << std::endl; +#endif + + if (m_tracer) { + string::size_type len = line.length(); + while (len != 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) --len; + m_tracer->traceReceive(line.substr(0, len)); + } + + return (line); + } + + /** Fill in the input buffer with data available from the socket stream. + * The function blocks until some data is available. + */ + void read() { + + string receiveBuffer; + + shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); + shared_ptr <socket> sok = m_socket.lock(); + + if (toh) { + toh->resetTimeOut(); + } + + while (receiveBuffer.empty()) { + + // Check whether the time-out delay is elapsed + if (toh && toh->isTimeOut()) { + if (!toh->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + } + + // We have received data: reset the time-out counter + sok->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + + if (sok->getStatus() & socket::STATUS_WANT_WRITE) { + sok->waitForWrite(); + } else { + sok->waitForRead(); + } + + continue; + } + + // We have received data ... + if (toh) { + toh->resetTimeOut(); + } + } + + m_buffer += receiveBuffer; + } + + + void readLiteral(literalHandler::target& buffer, size_t count) { + + size_t len = 0; + string receiveBuffer; + + shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); + shared_ptr <socket> sok = m_socket.lock(); + + if (m_progress) { + m_progress->start(count); + } + + if (toh) { + toh->resetTimeOut(); + } + + if (!m_buffer.empty()) { + + if (m_buffer.length() > count) { + + buffer.putData(string(m_buffer.begin(), m_buffer.begin() + count)); + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + count); + len = count; + + } else { + + len += m_buffer.length(); + buffer.putData(m_buffer); + m_buffer.clear(); + } + } + + while (len < count) { + + // Check whether the time-out delay is elapsed + if (toh && toh->isTimeOut()) { + + if (!toh->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + toh->resetTimeOut(); + } + + // Receive data from the socket + sok->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + + if (sok->getStatus() & socket::STATUS_WANT_WRITE) { + sok->waitForWrite(); + } else { + sok->waitForRead(); + } + + continue; + } + + // We have received data: reset the time-out counter + if (toh) { + toh->resetTimeOut(); + } + + if (len + receiveBuffer.length() > count) { + + const size_t remaining = count - len; + + // Get the needed amount of data + buffer.putData(string(receiveBuffer.begin(), receiveBuffer.begin() + remaining)); + + // Put the remaining data into the internal response buffer + receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + remaining); + m_buffer += receiveBuffer; + + len = count; + + } else { + + buffer.putData(receiveBuffer); + len += receiveBuffer.length(); + } + + // Notify progress + if (m_progress) { + m_progress->progress(len, count); + } + } + + if (m_tracer) { + m_tracer->traceReceiveBytes(count); + } + + if (m_progress) { + m_progress->stop(count); + } + } +}; + + +} // imap +} // net +} // vmime + + +#undef VIMAP_PARSER_FAIL +#undef VIMAP_PARSER_FAIL_UNLESS +#undef VIMAP_PARSER_CHECK +#undef VIMAP_PARSER_TRY_CHECK +#undef VIMAP_PARSER_GET +#undef VIMAP_PARSER_GET +#undef VIMAP_PARSER_TRY_GET +#undef VIMAP_PARSER_TRY_GET +#undef VIMAP_PARSER_GET_PUSHBACK +#undef VIMAP_PARSER_CHECK_WITHARG +#undef VIMAP_PARSER_TRY_CHECK_WITHARG + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPSStore.cpp b/vmime-master/src/vmime/net/imap/IMAPSStore.cpp new file mode 100644 index 0000000..dd9c318 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPSStore.cpp @@ -0,0 +1,82 @@ +// +// 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/IMAPSStore.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPSStore::IMAPSStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : IMAPStore(sess, auth, true) { + +} + + +IMAPSStore::~IMAPSStore() { + +} + + +const string IMAPSStore::getProtocolName() const { + + return "imaps"; +} + + + +// Service infos + +IMAPServiceInfos IMAPSStore::sm_infos(true); + + +const serviceInfos& IMAPSStore::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& IMAPSStore::getInfos() const { + + return sm_infos; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPSStore.hpp b/vmime-master/src/vmime/net/imap/IMAPSStore.hpp new file mode 100644 index 0000000..e18a067 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPSStore.hpp @@ -0,0 +1,70 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPSSTORE_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPSSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/imap/IMAPStore.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** IMAPS store service. + */ +class VMIME_EXPORT IMAPSStore : public IMAPStore { + +public: + + IMAPSStore(const shared_ptr <session>& sess, const shared_ptr <security::authenticator>& auth); + ~IMAPSStore(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static IMAPServiceInfos sm_infos; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPSSTORE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/imap/IMAPServiceInfos.cpp b/vmime-master/src/vmime/net/imap/IMAPServiceInfos.cpp new file mode 100644 index 0000000..baed6ba --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPServiceInfos.cpp @@ -0,0 +1,135 @@ +// +// 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/IMAPServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPServiceInfos::IMAPServiceInfos(const bool imaps) + : m_imaps(imaps) { + +} + + +const string IMAPServiceInfos::getPropertyPrefix() const { + + if (m_imaps) { + return "store.imaps."; + } else { + return "store.imap."; + } +} + + +const IMAPServiceInfos::props& IMAPServiceInfos::getProperties() const { + + static props imapProps = { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "143"), + }; + + static props imapsProps = { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "993"), + }; + + return m_imaps ? imapsProps : imapProps; +} + + +const std::vector <serviceInfos::property> IMAPServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_imaps) { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + + return list; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPServiceInfos.hpp b/vmime-master/src/vmime/net/imap/IMAPServiceInfos.hpp new file mode 100644 index 0000000..73fca7a --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPServiceInfos.hpp @@ -0,0 +1,89 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** Information about IMAP service. + */ +class VMIME_EXPORT IMAPServiceInfos : public serviceInfos { + +public: + + IMAPServiceInfos(const bool imaps); + + struct props { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; + +private: + + const bool m_imaps; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/imap/IMAPStore.cpp b/vmime-master/src/vmime/net/imap/IMAPStore.cpp new file mode 100644 index 0000000..eafa444 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPStore.cpp @@ -0,0 +1,287 @@ +// +// 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/IMAPStore.hpp" +#include "vmime/net/imap/IMAPFolder.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include <map> + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPStore::IMAPStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured +) + : store(sess, getInfosInstance(), auth), + m_connection(null), + m_isIMAPS(secured) { + +} + + +IMAPStore::~IMAPStore() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string IMAPStore::getProtocolName() const { + + return "imap"; +} + + +shared_ptr <folder> IMAPStore::getRootFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return make_shared <IMAPFolder>( + folder::path(), + dynamicCast <IMAPStore>(shared_from_this()), + shared_ptr <folderAttributes>() + ); +} + + +shared_ptr <folder> IMAPStore::getDefaultFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return make_shared <IMAPFolder>( + folder::path::component("INBOX"), + dynamicCast <IMAPStore>(shared_from_this()), + shared_ptr <folderAttributes>() + ); +} + + +shared_ptr <folder> IMAPStore::getFolder(const folder::path& path) { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return make_shared <IMAPFolder>( + path, + dynamicCast <IMAPStore>(shared_from_this()), + shared_ptr <folderAttributes>() + ); +} + + +bool IMAPStore::isValidFolderName(const folder::path::component& /* name */) const { + + return true; +} + + +void IMAPStore::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + m_connection = make_shared <IMAPConnection>( + dynamicCast <IMAPStore>(shared_from_this()), getAuthenticator() + ); + + m_connection->connect(); +} + + +bool IMAPStore::isConnected() const { + + return m_connection && m_connection->isConnected(); +} + + +bool IMAPStore::isIMAPS() const { + + return m_isIMAPS; +} + + +bool IMAPStore::isSecuredConnection() const { + + if (!m_connection) { + return false; + } + + return m_connection->isSecuredConnection(); +} + + +shared_ptr <connectionInfos> IMAPStore::getConnectionInfos() const { + + if (!m_connection) { + return null; + } + + return m_connection->getConnectionInfos(); +} + + +shared_ptr <IMAPConnection> IMAPStore::getConnection() { + + return m_connection; +} + + +void IMAPStore::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + for (std::list <IMAPFolder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) { + + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + m_connection->disconnect(); + + m_connection = null; +} + + +void IMAPStore::noop() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + IMAPCommand::NOOP()->send(m_connection); + + scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done->response_tagged-> + resp_cond_state->status != IMAPParser::resp_cond_state::OK) { + + throw exceptions::command_error("NOOP", resp->getErrorLog()); + } + + + for (std::list <IMAPFolder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) { + + if ((*it)->isOpen()) { + (*it)->noop(); + } + } +} + + +shared_ptr <IMAPConnection> IMAPStore::connection() { + + return m_connection; +} + + +void IMAPStore::registerFolder(IMAPFolder* folder) { + + m_folders.push_back(folder); +} + + +void IMAPStore::unregisterFolder(IMAPFolder* folder) { + + std::list <IMAPFolder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + + if (it != m_folders.end()) { + m_folders.erase(it); + } +} + + +int IMAPStore::getCapabilities() const { + + return CAPABILITY_CREATE_FOLDER | + CAPABILITY_RENAME_FOLDER | + CAPABILITY_ADD_MESSAGE | + CAPABILITY_COPY_MESSAGE | + CAPABILITY_DELETE_MESSAGE | + CAPABILITY_PARTIAL_FETCH | + CAPABILITY_MESSAGE_FLAGS | + CAPABILITY_EXTRACT_PART; +} + + + +// Service infos + +IMAPServiceInfos IMAPStore::sm_infos(false); + + +const serviceInfos& IMAPStore::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& IMAPStore::getInfos() const { + + return sm_infos; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPStore.hpp b/vmime-master/src/vmime/net/imap/IMAPStore.hpp new file mode 100644 index 0000000..018c195 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPStore.hpp @@ -0,0 +1,124 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/store.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/folder.hpp" + +#include "vmime/net/imap/IMAPServiceInfos.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPParser; +class IMAPTag; +class IMAPFolder; + + +/** IMAP store service. + */ +class VMIME_EXPORT IMAPStore : public store { + + friend class IMAPFolder; + friend class IMAPMessage; + friend class IMAPConnection; + +public: + + IMAPStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured = false + ); + + ~IMAPStore(); + + const string getProtocolName() const; + + shared_ptr <folder> getDefaultFolder(); + shared_ptr <folder> getRootFolder(); + shared_ptr <folder> getFolder(const folder::path& path); + + bool isValidFolderName(const folder::path::component& name) const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + int getCapabilities() const; + + bool isIMAPS() const; + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + shared_ptr <IMAPConnection> getConnection(); + +protected: + + // Connection + shared_ptr <IMAPConnection> m_connection; + + + + shared_ptr <IMAPConnection> connection(); + + + void registerFolder(IMAPFolder* folder); + void unregisterFolder(IMAPFolder* folder); + + std::list <IMAPFolder*> m_folders; + + const bool m_isIMAPS; // Use IMAPS + + + static IMAPServiceInfos sm_infos; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPTag.cpp b/vmime-master/src/vmime/net/imap/IMAPTag.cpp new file mode 100644 index 0000000..52cd212 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPTag.cpp @@ -0,0 +1,148 @@ +// +// 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/IMAPTag.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +const int IMAPTag::sm_maxNumber = 52 * 10 * 10 * 10; + + +IMAPTag::IMAPTag(const int number) + : m_number(number) { + + m_tag.resize(4); + generate(); +} + + +IMAPTag::IMAPTag(const IMAPTag& tag) + : object(), + m_number(tag.m_number) { + + m_tag.resize(4); + generate(); +} + + +IMAPTag::IMAPTag() + : m_number(1) { + + m_tag.resize(4); + generate(); +} + + +IMAPTag& IMAPTag::operator++() { + + ++m_number; + + if (m_number >= sm_maxNumber) { + m_number = 1; + } + + generate(); + + return *this; +} + + +const IMAPTag IMAPTag::operator++(int) { + + IMAPTag old(*this); + operator++(); + return old; +} + + +bool IMAPTag::operator<(const IMAPTag &other) const { + + return m_number < other.m_number; +} + + +bool IMAPTag::operator==(const IMAPTag &other) const { + + return m_number == other.m_number; +} + + +bool IMAPTag::operator!=(const IMAPTag &other) const { + + return m_number != other.m_number; +} + + +bool IMAPTag::operator==(const std::string &tag) const { + + return m_tag == tag; +} + + +int IMAPTag::maximumNumber() const { + + return sm_maxNumber - 1; +} + + +int IMAPTag::number() const { + + return m_number; +} + + +IMAPTag::operator string() const +{ + return m_tag; +} + + +void IMAPTag::generate() { + + static const char prefixChars[53] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + m_tag[0] = prefixChars[m_number / 1000]; + m_tag[1] = static_cast <char>('0' + (m_number % 1000) / 100); + m_tag[2] = static_cast <char>('0' + (m_number % 100) / 10); + m_tag[3] = static_cast <char>('0' + m_number % 10); +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPTag.hpp b/vmime-master/src/vmime/net/imap/IMAPTag.hpp new file mode 100644 index 0000000..cbab942 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPTag.hpp @@ -0,0 +1,85 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPTAG_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPTAG_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class VMIME_EXPORT IMAPTag : public object { + +private: + + IMAPTag(const int number); + +public: + + IMAPTag(); + IMAPTag(const IMAPTag& tag); + + IMAPTag& operator++(); // ++IMAPTag + const IMAPTag operator++(int); // IMAPTag++ + + int maximumNumber() const; + int number() const; + + operator string() const; + + bool operator<(const IMAPTag &other) const; + bool operator==(const IMAPTag &other) const; + bool operator!=(const IMAPTag &other) const; + + bool operator==(const std::string &tag) const; + +private: + + void generate(); + + static const int sm_maxNumber; + + int m_number; + string m_tag; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPTAG_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/IMAPUtils.cpp b/vmime-master/src/vmime/net/imap/IMAPUtils.cpp new file mode 100644 index 0000000..59b1e18 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPUtils.cpp @@ -0,0 +1,854 @@ +// +// 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/IMAPUtils.hpp" +#include "vmime/net/imap/IMAPStore.hpp" + +#include "vmime/net/message.hpp" +#include "vmime/net/folder.hpp" + +#include <sstream> +#include <iterator> +#include <algorithm> + + +namespace vmime { +namespace net { +namespace imap { + + +// static +const string IMAPUtils::quoteString(const string& text) { + + // + // ATOM_CHAR ::= <any CHAR except atom_specials> + // + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / + // list_wildcards / quoted_specials + // + // list_wildcards ::= "%" / "*" + // + // quoted_specials ::= <"> / "\" + // + // CHAR ::= <any 7-bit US-ASCII character except NUL, + // 0x01 - 0x7f> + // + // CTL ::= <any ASCII control character and DEL, + // 0x00 - 0x1f, 0x7f> + // + + bool needQuoting = text.empty(); + + for (string::const_iterator it = text.begin() ; + !needQuoting && it != text.end() ; ++it) { + + const unsigned char c = *it; + + switch (c) { + + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': + case '*': + case '"': + case '\\': + + needQuoting = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) { + needQuoting = true; + } + } + } + + if (needQuoting) { + + string quoted; + quoted.reserve((text.length() * 3) / 2 + 2); + + quoted += '"'; + + for (string::const_iterator it = text.begin() ; it != text.end() ; ++it) { + + const unsigned char c = *it; + + if (c == '\\' || c == '"') { + quoted += '\\'; + } + + quoted += c; + } + + quoted += '"'; + + return quoted; + + } else { + + return text; + } +} + + +const string IMAPUtils::pathToString( + const char hierarchySeparator, + const folder::path& path +) { + + string result; + + for (size_t i = 0 ; i < path.getSize() ; ++i) { + + if (i > 0) result += hierarchySeparator; + result += toModifiedUTF7(hierarchySeparator, path[i]); + } + + return (result); +} + + +const folder::path IMAPUtils::stringToPath( + const char hierarchySeparator, + const string& str +) { + + folder::path result; + string::const_iterator begin = str.begin(); + + for (string::const_iterator it = str.begin() ; it != str.end() ; ++it) { + + if (*it == hierarchySeparator) { + result /= fromModifiedUTF7(string(begin, it)); + begin = it + 1; + } + } + + if (begin != str.end()) { + result /= fromModifiedUTF7(string(begin, str.end())); + } + + return result; +} + + +const string IMAPUtils::toModifiedUTF7( + const char hierarchySeparator, + const folder::path::component& text +) { + + // We will replace the hierarchy separator with an equivalent + // UTF-7 sequence, so we compute it here... + const char base64alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,="; + + const unsigned int hs = static_cast <unsigned int>(static_cast <unsigned char>(hierarchySeparator)); + + string hsUTF7; + hsUTF7.resize(3); + + hsUTF7[0] = base64alphabet[0]; + hsUTF7[1] = base64alphabet[(hs & 0xF0) >> 4]; + hsUTF7[2] = base64alphabet[(hs & 0x0F) << 2]; + + // iconv() is buggy with UTF-8 to UTF-7 conversion, so we do it "by hand". + // This code is largely inspired from "imap/utf7.c", in mutt 1.4. + // Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org> + + // WARNING: This may throw "exceptions::charset_conv_error" + const string cvt = text.getConvertedText(charset(charsets::UTF_8)); + + // In the worst case we convert 2 chars to 7 chars. + // For example: "\x10&\x10&..." -> "&ABA-&-&ABA-&-...". + string out; + out.reserve((cvt.length() / 2) * 7 + 6); + + int b = 0, k = 0; + bool base64 = false; + + size_t remaining = cvt.length(); + + for (size_t i = 0, len = cvt.length() ; i < len ; ) { + + const unsigned char c = cvt[i]; + + // Replace hierarchy separator with an equivalent UTF-7 Base64 sequence + if (!base64 && c == hierarchySeparator) { + + out += "&" + hsUTF7 + "-"; + + ++i; + --remaining; + continue; + } + + size_t n = 0; + int ch = 0; + + if (c < 0x80) { + ch = c, n = 0; + } else if (c < 0xc2) { + return ""; + } else if (c < 0xe0) { + ch = c & 0x1f, n = 1; + } else if (c < 0xf0) { + ch = c & 0x0f, n = 2; + } else if (c < 0xf8) { + ch = c & 0x07, n = 3; + } else if (c < 0xfc) { + ch = c & 0x03, n = 4; + } else if (c < 0xfe) { + ch = c & 0x01, n = 5; + } else { + return ""; + } + + if (n > remaining) { + return ""; // error + } + + ++i; + --remaining; + + for (size_t j = 0 ; j < n ; j++) { + + if ((cvt[i + j] & 0xc0) != 0x80) { + return ""; // error + } + + ch = (ch << 6) | (cvt[i + j] & 0x3f); + } + + if (n > 1 && !(ch >> (n * 5 + 1))) { + return ""; // error + } + + i += n; + remaining -= n; + + if (ch < 0x20 || ch >= 0x7f) { + + if (!base64) { + out += '&'; + base64 = true; + b = 0; + k = 10; + } + + if (ch & ~0xffff) { + ch = 0xfffe; + } + + out += base64alphabet[b | ch >> k]; + + k -= 6; + + for ( ; k >= 0 ; k -= 6) { + out += base64alphabet[(ch >> k) & 0x3f]; + } + + b = (ch << (-k)) & 0x3f; + k += 16; + + } else { + + if (base64) { + + if (k > 10) { + out += base64alphabet[b]; + } + + out += '-'; + base64 = false; + } + + out += static_cast <char>(ch); + + if (ch == '&') { + out += '-'; + } + } + } + + if (base64) { + + if (k > 10) { + out += base64alphabet[b]; + } + + out += '-'; + } + + return (out); +} + + +const folder::path::component IMAPUtils::fromModifiedUTF7(const string& text) { + + // Transcode from modified UTF-7 (RFC-2060). + string out; + out.reserve(text.length()); + + bool inB64sequence = false; + bool plusOutput = false; + unsigned char prev = 0; + + for (string::const_iterator it = text.begin() ; it != text.end() ; ++it) { + + const unsigned char c = *it; + + switch (c) { + + // Start of Base64 sequence + case '&': { + + if (!inB64sequence) { + inB64sequence = true; + plusOutput = false; + } else { + out += '&'; + } + + break; + } + // End of Base64 sequence (or "&-" --> "&") + case '-': { + + if (inB64sequence && prev == '&') { // special case "&-" --> "&" + out += '&'; + } else { + out += '-'; + } + + inB64sequence = false; + break; + } + // ',' is used instead of '/' in modified Base64 + case ',': { + + if (inB64sequence && !plusOutput) { + out += '+'; + plusOutput = true; + } + + out += (inB64sequence ? '/' : ','); + break; + } + default: { + + if (inB64sequence && !plusOutput) { + out += '+'; + plusOutput = true; + } + + out += c; + break; + } + } + + prev = c; + } + + // Store it as UTF-8 by default + string cvt; + charset::convert(out, cvt, charset(charsets::UTF_7), charset(charsets::UTF_8)); + + return folder::path::component(cvt, charset(charsets::UTF_8)); +} + + +// static +void IMAPUtils::mailboxFlagsToFolderAttributes( + const shared_ptr <const IMAPConnection>& cnt, + const folder::path &path, + const IMAPParser::mailbox_flag_list& list, + folderAttributes& attribs +) { + + int specialUse = folderAttributes::SPECIALUSE_NONE; + int type = folderAttributes::TYPE_CONTAINS_MESSAGES | folderAttributes::TYPE_CONTAINS_FOLDERS; + int flags = 0; + + // If CHILDREN extension (RFC-3348) is not supported, assume folder has children + // as we have no hint about it + if (!cnt->hasCapability("CHILDREN")) { + flags |= folderAttributes::FLAG_HAS_CHILDREN; + } + + for (auto it = list.flags.begin() ; it != list.flags.end() ; ++it) { + + switch ((*it)->type) { + + case IMAPParser::mailbox_flag::NOSELECT: + + type &= ~folderAttributes::TYPE_CONTAINS_MESSAGES; + flags |= folderAttributes::FLAG_NO_OPEN; + break; + + case IMAPParser::mailbox_flag::NOINFERIORS: + case IMAPParser::mailbox_flag::HASNOCHILDREN: + + flags &= ~folderAttributes::FLAG_HAS_CHILDREN; + break; + + case IMAPParser::mailbox_flag::HASCHILDREN: + + flags |= folderAttributes::FLAG_HAS_CHILDREN; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_ALL: + + specialUse = folderAttributes::SPECIALUSE_ALL; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_ARCHIVE: + + specialUse = folderAttributes::SPECIALUSE_ARCHIVE; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_DRAFTS: + + specialUse = folderAttributes::SPECIALUSE_DRAFTS; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_FLAGGED: + + specialUse = folderAttributes::SPECIALUSE_FLAGGED; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_JUNK: + + specialUse = folderAttributes::SPECIALUSE_JUNK; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_SENT: + + specialUse = folderAttributes::SPECIALUSE_SENT; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_TRASH: + + specialUse = folderAttributes::SPECIALUSE_TRASH; + break; + + case IMAPParser::mailbox_flag::SPECIALUSE_IMPORTANT: + + specialUse = folderAttributes::SPECIALUSE_IMPORTANT; + break; + + default: + + break; + } + } + + if (path.getSize() == 1 && path.getLastComponent().getBuffer() == "INBOX") { + specialUse = folderAttributes::SPECIALUSE_INBOX; + } + + attribs.setSpecialUse(specialUse); + attribs.setType(type); + attribs.setFlags(flags); +} + + +int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list& list) { + + int flags = 0; + + for (auto &flag : list.flags) { + + switch (flag->type) { + + case IMAPParser::flag::ANSWERED: + + flags |= message::FLAG_REPLIED; + break; + + case IMAPParser::flag::FLAGGED: + + flags |= message::FLAG_MARKED; + break; + + case IMAPParser::flag::DELETED: + + flags |= message::FLAG_DELETED; + break; + + case IMAPParser::flag::SEEN: + + flags |= message::FLAG_SEEN; + break; + + case IMAPParser::flag::DRAFT: + + flags |= message::FLAG_DRAFT; + break; + + default: + //case IMAPParser::flag::UNKNOWN: + + break; + } + } + + return flags; +} + + +// static +const std::vector <string> IMAPUtils::messageFlagList(const int flags) { + + std::vector <string> flagList; + + if (flags == -1) { + return flagList; // default flags + } + + if (flags & message::FLAG_REPLIED) flagList.push_back("\\Answered"); + if (flags & message::FLAG_MARKED) flagList.push_back("\\Flagged"); + if (flags & message::FLAG_DELETED) flagList.push_back("\\Deleted"); + if (flags & message::FLAG_SEEN) flagList.push_back("\\Seen"); + if (flags & message::FLAG_DRAFT) flagList.push_back("\\Draft"); + + return flagList; +} + + +// static +const string IMAPUtils::dateTime(const vmime::datetime& date) { + + std::ostringstream res; + res.imbue(std::locale::classic()); + + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich + res << '"'; + + // Date + if (date.getDay() < 10) res << ' '; + res << date.getDay(); + + res << '-'; + + static const char* monthNames[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + res << monthNames[std::min(std::max(date.getMonth() - 1, 0), 11)]; + + res << '-'; + + if (date.getYear() < 10) res << '0'; + if (date.getYear() < 100) res << '0'; + if (date.getYear() < 1000) res << '0'; + res << date.getYear(); + + res << ' '; + + // Time + if (date.getHour() < 10) res << '0'; + res << date.getHour() << ':'; + + if (date.getMinute() < 10) res << '0'; + res << date.getMinute() << ':'; + + if (date.getSecond() < 10) res << '0'; + res << date.getSecond(); + + res << ' '; + + // Zone + const int zs = (date.getZone() < 0 ? -1 : 1); + const int zh = (date.getZone() * zs) / 60; + const int zm = (date.getZone() * zs) % 60; + + res << (zs < 0 ? '-' : '+'); + + if (zh < 10) res << '0'; + res << zh; + + if (zm < 10) res << '0'; + res << zm; + + res << '"'; + + return res.str(); +} + + +// static +shared_ptr <IMAPCommand> IMAPUtils::buildFetchCommand( + const shared_ptr <IMAPConnection>& cnt, + const messageSet& msgs, + const fetchAttributes& options +) { + + // Example: + // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) + // S: * 2 FETCH .... + // S: * 3 FETCH .... + // S: * 4 FETCH .... + // S: A654 OK FETCH completed + + std::vector <string> items; + + if (options.has(fetchAttributes::SIZE)) { + items.push_back("RFC822.SIZE"); + } + + if (options.has(fetchAttributes::FLAGS)) { + items.push_back("FLAGS"); + } + + if (options.has(fetchAttributes::STRUCTURE)) { + items.push_back("BODYSTRUCTURE"); + } + + if (options.has(fetchAttributes::UID)) { + + items.push_back("UID"); + + // Also fetch MODSEQ if CONDSTORE is supported + if (cnt && cnt->hasCapability("CONDSTORE") && !cnt->isMODSEQDisabled()) { + items.push_back("MODSEQ"); + } + } + + if (options.has(fetchAttributes::FULL_HEADER)) { + + items.push_back("RFC822.HEADER"); + + } else { + + if (options.has(fetchAttributes::ENVELOPE)) { + items.push_back("ENVELOPE"); + } + + std::vector <string> headerFields; + + if (options.has(fetchAttributes::CONTENT_INFO)) { + headerFields.push_back("CONTENT_TYPE"); + } + + if (options.has(fetchAttributes::IMPORTANCE)) { + headerFields.push_back("IMPORTANCE"); + headerFields.push_back("X-PRIORITY"); + } + + // Also add custom header fields to fetch, if any + const std::vector <string> customHeaderFields = options.getHeaderFields(); + std::copy(customHeaderFields.begin(), customHeaderFields.end(), std::back_inserter(headerFields)); + + if (!headerFields.empty()) { + + string list; + + for (std::vector <string>::iterator it = headerFields.begin() ; + it != headerFields.end() ; ++it) { + + if (it != headerFields.begin()) { + list += " "; + } + + list += *it; + } + + items.push_back("BODY[HEADER.FIELDS (" + list + ")]"); + } + } + + return IMAPCommand::FETCH(msgs, items); +} + + +// static +void IMAPUtils::convertAddressList( + const IMAPParser::address_list& src, + mailboxList& dest +) { + + for (auto& addr : src.addresses) { + + text name; + text::decodeAndUnfold(addr->addr_name->value, &name); + + string email = addr->addr_mailbox->value + + "@" + addr->addr_host->value; + + dest.appendMailbox(make_shared <mailbox>(name, email)); + } +} + + + +class IMAPUIDMessageSetEnumerator : public messageSetEnumerator { + +public: + + IMAPUIDMessageSetEnumerator() + : m_first(true) { + + m_oss.imbue(std::locale::classic()); + } + + void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range) { + + if (!m_first) { + m_oss << ","; + } + + if (range.getFirst() == range.getLast()) { + m_oss << range.getFirst(); + } else if (range.getLast() == size_t(-1)) { + m_oss << range.getFirst() << ":*"; + } else { + m_oss << range.getFirst() << ":" << range.getLast(); + } + + m_first = false; + } + + void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& range) { + + if (!m_first) { + m_oss << ","; + } + + if (range.getFirst() == range.getLast()) { + m_oss << range.getFirst(); + } else if (range.getLast() == size_t(-1)) { + m_oss << range.getFirst() << ":*"; + } else { + m_oss << range.getFirst() << ":" << range.getLast(); + } + + m_first = false; + } + + const std::string str() const { + + return m_oss.str(); + } + +private: + + std::ostringstream m_oss; + bool m_first; +}; + + +class IMAPMessageSetEnumerator : public messageSetEnumerator { + +public: + + void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range) { + + for (size_t i = range.getFirst(), last = range.getLast() ; i <= last ; ++i) { + m_list.push_back(i); + } + } + + void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */) { + + // Not used + } + + const std::vector <size_t>& list() const { + + return m_list; + } + +public: + + std::vector <size_t> m_list; +}; + + + +// static +const string IMAPUtils::messageSetToSequenceSet(const messageSet& msgs) { + + IMAPUIDMessageSetEnumerator en; + msgs.enumerate(en); + + return en.str(); +} + + +// static +messageSet IMAPUtils::buildMessageSet(const IMAPParser::uid_set& uidSetRef) { + + messageSet set = messageSet::empty(); + + const IMAPParser::uid_set* uidSet = &uidSetRef; + + for ( ; uidSet ; uidSet = uidSet->next_uid_set.get()) { + + if (uidSet->uid_range) { + + set.addRange( + UIDMessageRange( + message::uid(uidSet->uid_range->uniqueid1->value), + message::uid(uidSet->uid_range->uniqueid2->value) + ) + ); + + } else { + + set.addRange( + UIDMessageRange( + message::uid(uidSet->uniqueid->value) + ) + ); + } + } + + return set; +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + diff --git a/vmime-master/src/vmime/net/imap/IMAPUtils.hpp b/vmime-master/src/vmime/net/imap/IMAPUtils.hpp new file mode 100644 index 0000000..d4bd135 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/IMAPUtils.hpp @@ -0,0 +1,144 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/types.hpp" +#include "vmime/dateTime.hpp" + +#include "vmime/net/folder.hpp" +#include "vmime/net/message.hpp" +#include "vmime/net/imap/IMAPParser.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" + +#include "vmime/mailboxList.hpp" + +#include <vector> + + +namespace vmime { +namespace net { +namespace imap { + + +class VMIME_EXPORT IMAPUtils +{ +public: + + static const string pathToString(const char hierarchySeparator, const folder::path& path); + static const folder::path stringToPath(const char hierarchySeparator, const string& str); + + static const string toModifiedUTF7(const char hierarchySeparator, const folder::path::component& text); + static const folder::path::component fromModifiedUTF7(const string& text); + + /** Quote string if it contains IMAP-special characters. + * + * @param text string to quote + * @return quoted string + */ + static const string quoteString(const string& text); + + /** Parse mailbox flags and fill in folder attributes. + * + * @param cnt reference to current IMAP connection (for testing capabilities) + * @param path folder full path + * @param list list of mailbox flags + * @param attribs reference to an object holding folder attributes + */ + static void mailboxFlagsToFolderAttributes( + const shared_ptr <const IMAPConnection>& cnt, + const folder::path &path, + const IMAPParser::mailbox_flag_list& list, + folderAttributes& attribs + ); + + static int messageFlagsFromFlags(const IMAPParser::flag_list& list); + + static const std::vector <string> messageFlagList(const int flags); + + /** Format a date/time to IMAP date/time format. + * + * @param date date/time to format + * @return IMAP-formatted date/time + */ + static const string dateTime(const vmime::datetime& date); + + /** Construct a fetch request for the specified messages, designated + * either by their sequence numbers or their UIDs. + * + * @param cnt connection + * @param msgs message set + * @param options fetch options + * @return fetch request + */ + static shared_ptr <IMAPCommand> buildFetchCommand( + const shared_ptr <IMAPConnection>& cnt, + const messageSet& msgs, + const fetchAttributes& options + ); + + /** Convert a parser-style address list to a mailbox list. + * + * @param src input address list + * @param dest output mailbox list + */ + static void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest); + + /** Returns an IMAP-formatted sequence set given a message set. + * + * @param msgs message set + * @return IMAP sequence set (eg. "1:5,7,15:*") + */ + static const string messageSetToSequenceSet(const messageSet& msgs); + + /** Constructs a message set from a parser 'uid_set' structure. + * + * @param uidSet UID set, as returned by the parser + * @return message set + */ + static messageSet buildMessageSet(const IMAPParser::uid_set& uidSet); + +private: + + static const string buildFetchRequestImpl + (const shared_ptr <IMAPConnection>& cnt, const string& mode, const string& set, const int options); +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/imap/imap.hpp b/vmime-master/src/vmime/net/imap/imap.hpp new file mode 100644 index 0000000..f25baa4 --- /dev/null +++ b/vmime-master/src/vmime/net/imap/imap.hpp @@ -0,0 +1,35 @@ +// +// 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. +// + +#ifndef VMIME_NET_IMAP_IMAP_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAP_HPP_INCLUDED + + +#include "vmime/net/imap/IMAPFolder.hpp" +#include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPMessage.hpp" +#include "vmime/net/imap/IMAPStore.hpp" +#include "vmime/net/imap/IMAPSStore.hpp" + + +#endif // VMIME_NET_IMAP_IMAP_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.cpp b/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.cpp new file mode 100644 index 0000000..e611949 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.cpp @@ -0,0 +1,569 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/format/courierMaildirFormat.hpp" + +#include "vmime/net/maildir/maildirStore.hpp" +#include "vmime/net/maildir/maildirUtils.hpp" + +#include "vmime/platform.hpp" + + +namespace vmime { +namespace net { +namespace maildir { +namespace format { + + +courierMaildirFormat::courierMaildirFormat(const shared_ptr <context>& ctx) + : maildirFormat(ctx) { + +} + + +const string courierMaildirFormat::getName() const { + + return "courier"; +} + + +void courierMaildirFormat::createFolder(const folder::path& path) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + if (!fsf->isValidPath(folderPathToFileSystemPath(path, ROOT_DIRECTORY))) { + throw exceptions::invalid_folder_name(); + } + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + ); + + shared_ptr <utility::file> newDir = fsf->create( + folderPathToFileSystemPath(path, NEW_DIRECTORY) + ); + shared_ptr <utility::file> tmpDir = fsf->create( + folderPathToFileSystemPath(path, TMP_DIRECTORY) + ); + shared_ptr <utility::file> curDir = fsf->create( + folderPathToFileSystemPath(path, CUR_DIRECTORY) + ); + + rootDir->createDirectory(true); + + newDir->createDirectory(false); + tmpDir->createDirectory(false); + curDir->createDirectory(false); + + shared_ptr <utility::file> maildirFile = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + / utility::file::path::component("maildirfolder") + ); + + maildirFile->createFile(); +} + + +void courierMaildirFormat::destroyFolder(const folder::path& path) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + // Recursively delete directories of subfolders + const std::vector <folder::path> folders = listFolders(path, true); + + for (std::vector <folder::path>::size_type i = 0, n = folders.size() ; i < n ; ++i) { + + maildirUtils::recursiveFSDelete( + fsf->create(folderPathToFileSystemPath(folders[i], ROOT_DIRECTORY)) + ); + } + + // Recursively delete the directory of this folder + maildirUtils::recursiveFSDelete( + fsf->create(folderPathToFileSystemPath(path, ROOT_DIRECTORY)) + ); +} + + +void courierMaildirFormat::renameFolder( + const folder::path& oldPath, + const folder::path& newPath +) { + + const std::vector <folder::path> folders = listFolders(oldPath, true); + + for (std::vector <folder::path>::size_type i = 0, n = folders.size() ; i < n ; ++i) { + + const folder::path folderOldPath = folders[i]; + + folder::path folderNewPath = folderOldPath; + folderNewPath.renameParent(oldPath, newPath); + + renameFolderImpl(folderOldPath, folderNewPath); + } + + renameFolderImpl(oldPath, newPath); +} + + +void courierMaildirFormat::renameFolderImpl( + const folder::path& oldPath, + const folder::path& newPath +) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + const utility::file::path oldFSPath = + folderPathToFileSystemPath(oldPath, ROOT_DIRECTORY); + + const utility::file::path newFSPath = + folderPathToFileSystemPath(newPath, ROOT_DIRECTORY); + + shared_ptr <utility::file> rootDir = fsf->create(oldFSPath); + rootDir->rename(newFSPath); +} + + +bool courierMaildirFormat::folderExists(const folder::path& path) const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + ); + + shared_ptr <utility::file> newDir = fsf->create( + folderPathToFileSystemPath(path, NEW_DIRECTORY) + ); + shared_ptr <utility::file> tmpDir = fsf->create( + folderPathToFileSystemPath(path, TMP_DIRECTORY) + ); + shared_ptr <utility::file> curDir = fsf->create( + folderPathToFileSystemPath(path, CUR_DIRECTORY) + ); + + shared_ptr <utility::file> maildirFile = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + / utility::file::path::component("maildirfolder") + ); + + bool exists = rootDir->exists() && rootDir->isDirectory() && + newDir->exists() && newDir->isDirectory() && + tmpDir->exists() && tmpDir->isDirectory() && + curDir->exists() && curDir->isDirectory(); + + // If this is not the root folder, then a file named "maildirfolder" + // must also be present in the directory + if (!path.isRoot()) { + exists = exists && maildirFile->exists() && maildirFile->isFile(); + } + + return exists; +} + + +bool courierMaildirFormat::folderHasSubfolders(const folder::path& path) const { + + std::vector <string> dirs; + return listDirectories(path, dirs, true); +} + + +const utility::file::path courierMaildirFormat::folderPathToFileSystemPath( + const folder::path& path, + const DirectoryType type +) const { + + // Virtual folder "/MyFolder/SubFolder" corresponds to physical + // directory "[store root]/.MyFolder.SubFolder" + utility::file::path fsPath = getContext()->getStore()->getFileSystemPath(); + + if (!path.isRoot()) { + + string folderComp; + + for (size_t i = 0, n = path.getSize() ; i < n ; ++i) { + folderComp += "." + toModifiedUTF7(path[i]); + } + + fsPath /= utility::file::path::component(folderComp); + } + + // Last component + switch (type) { + + case ROOT_DIRECTORY: + + // Nothing to add + break; + + case NEW_DIRECTORY: + + fsPath /= NEW_DIR; + break; + + case CUR_DIRECTORY: + + fsPath /= CUR_DIR; + break; + + case TMP_DIRECTORY: + + fsPath /= TMP_DIR; + break; + + case CONTAINER_DIRECTORY: + + // Not used + break; + } + + return fsPath; +} + + +const std::vector <folder::path> courierMaildirFormat::listFolders( + const folder::path& root, + const bool recursive +) const { + + // First, list directories + std::vector <string> dirs; + listDirectories(root, dirs, false); + + // Then, map directories to folders + std::vector <folder::path> folders; + + for (std::vector <string>::size_type i = 0, n = dirs.size() ; i < n ; ++i) { + + const string dir = dirs[i].substr(1) + "."; + folder::path path; + + for (size_t pos = dir.find("."), prev = 0 ; + pos != string::npos ; prev = pos + 1, pos = dir.find(".", pos + 1)) { + + const string comp = dir.substr(prev, pos - prev); + path /= fromModifiedUTF7(comp); + } + + if (recursive || path.getSize() == root.getSize() + 1) { + folders.push_back(path); + } + } + + return folders; +} + + +bool courierMaildirFormat::listDirectories( + const folder::path& root, + std::vector <string>& dirs, + const bool onlyTestForExistence +) const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + getContext()->getStore()->getFileSystemPath() + ); + + if (rootDir->exists()) { + + // To speed up things, and if we are not searching in root folder, + // search for directories with a common prefix + string base; + + if (!root.isRoot()) { + for (size_t i = 0, n = root.getSize() ; i < n ; ++i) { + base += "." + toModifiedUTF7(root[i]); + } + } + + // Enumerate directories + shared_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) { + + shared_ptr <utility::file> file = it->nextElement(); + + if (isSubfolderDirectory(*file)) { + + const string dir = file->getFullPath().getLastComponent().getBuffer(); + + if (base.empty() || (dir.length() > base.length() && dir.substr(0, base.length()) == base)) { + + dirs.push_back(dir); + + if (onlyTestForExistence) { + return true; + } + } + } + } + + } else { + + // No sub-folder + } + + std::sort(dirs.begin(), dirs.end()); + + return !dirs.empty(); +} + + +// static +bool courierMaildirFormat::isSubfolderDirectory(const utility::file& file) { + + // A directory which names starts with '.' may be a subfolder + if (file.isDirectory() && + file.getFullPath().getLastComponent().getBuffer().length() >= 1 && + file.getFullPath().getLastComponent().getBuffer()[0] == '.') { + + return true; + } + + return false; +} + + +// static +const string courierMaildirFormat::toModifiedUTF7(const folder::path::component& text) { + + // From http://www.courier-mta.org/?maildir.html: + // + // Folder names can contain any Unicode character, except for control + // characters. US-ASCII characters, U+0x0020 - U+0x007F, except for the + // period, forward-slash, and ampersand characters (U+0x002E, U+0x002F, + // and U+0x0026) represent themselves. The ampersand is represented by + // the two character sequence "&-". The period, forward slash, and non + // US-ASCII Unicode characters are represented using the UTF-7 character + // set, and encoded with a modified form of base64-encoding. + // + // The "&" character starts the modified base64-encoded sequence; the + // sequence is terminated by the "-" character. The sequence of 16-bit + // Unicode characters is written in big-endian order, and encoded using + // the base64-encoding method described in section 5.2 of RFC 1521, with + // the following modifications: + // + // * The "=" padding character is omitted. When decoding, an incomplete + // 16-bit character is discarded. + // + // * The comma character, "," is used in place of the "/" character in + // the base64 alphabet. + // + // For example, the word "Resume" with both "e"s being the e-acute + // character, U+0x00e9, is encoded as "R&AOk-sum&AOk-" (so a folder of + // that name would be a maildir subdirectory called ".R&AOk-sum&AOk-"). + // + + // Transcode path component to UTF-7 charset. + // WARNING: This may throw "exceptions::charset_conv_error" + const string cvt = text.getConvertedText(charset(charsets::UTF_7)); + + // Transcode to modified UTF-7 (RFC-2060). + string out; + out.reserve((cvt.length() * 3) / 2); + + bool inB64sequence = false; + + for (string::const_iterator it = cvt.begin() ; it != cvt.end() ; ++it) { + + const unsigned char c = *it; + + switch (c) { + + // Beginning of Base64 sequence: replace '+' with '&' + case '+': { + + if (!inB64sequence) { + inB64sequence = true; + out += '&'; + } else { + out += '+'; + } + + break; + } + // End of Base64 sequence + case '-': { + + inB64sequence = false; + out += '-'; + break; + } + // ',' is used instead of '/' in modified Base64, + // and simply UTF7-encoded out of a Base64 sequence + case '/': { + + if (inB64sequence) { + out += ','; + } else { + out += "&Lw-"; + } + + break; + } + // Encode period (should not happen in a Base64 sequence) + case '.': { + + out += "&Lg-"; + break; + } + // '&' (0x26) is represented by the two-octet sequence "&-" + case '&': { + + if (!inB64sequence) { + out += "&-"; + } else { + out += '&'; + } + + break; + } + default: { + + out += c; + break; + } + } + } + + return out; +} + + +// static +const folder::path::component courierMaildirFormat::fromModifiedUTF7(const string& text) { + + // Transcode from modified UTF-7 + string out; + out.reserve(text.length()); + + bool inB64sequence = false; + unsigned char prev = 0; + + for (string::const_iterator it = text.begin() ; it != text.end() ; ++it) { + + const unsigned char c = *it; + + switch (c) { + + // Start of Base64 sequence + case '&': { + + if (!inB64sequence) { + inB64sequence = true; + out += '+'; + } else { + out += '&'; + } + + break; + } + // End of Base64 sequence (or "&-" --> "&") + case '-': { + + if (inB64sequence && prev == '&') { + out += '&'; + } else { + out += '-'; + } + + inB64sequence = false; + break; + } + // ',' is used instead of '/' in modified Base64 + case ',': { + + out += (inB64sequence ? '/' : ','); + break; + } + default: { + + out += c; + break; + } + + } + + prev = c; + } + + // Store it as UTF-8 by default + string cvt; + charset::convert(out, cvt, charset(charsets::UTF_7), charset(charsets::UTF_8)); + + return folder::path::component(cvt, charset(charsets::UTF_8)); +} + + +bool courierMaildirFormat::supports() const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + getContext()->getStore()->getFileSystemPath() + ); + + if (rootDir->exists()) { + + // Try to find a file named "maildirfolder", which indicates + // the Maildir is in Courier format + shared_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) { + + shared_ptr <utility::file> file = it->nextElement(); + + if (isSubfolderDirectory(*file)) { + + shared_ptr <utility::file> folderFile = fsf->create( + file->getFullPath() / utility::file::path::component("maildirfolder") + ); + + if (folderFile->exists() && folderFile->isFile()) { + return true; + } + } + } + } + + return false; +} + + +} // format +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.hpp b/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.hpp new file mode 100644 index 0000000..7db1a83 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/format/courierMaildirFormat.hpp @@ -0,0 +1,127 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_FORMAT_COURIERMAILDIRFORMAT_HPP_INCLUDED +#define VMIME_NET_MAILDIR_FORMAT_COURIERMAILDIRFORMAT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/maildir/maildirFormat.hpp" + + +namespace vmime { +namespace net { +namespace maildir { +namespace format { + + +/** Reads Courier/QMail Maildir format. + */ +class VMIME_EXPORT courierMaildirFormat : public maildirFormat { + +public: + + courierMaildirFormat(const shared_ptr <context>& ctx); + + + /* Folder types: + * + * - ROOT_DIRECTORY: ~/Mail/.MyFolder + * - NEW_DIRECTORY: ~/Mail/.MyFolder/new + * - CUR_DIRECTORY: ~/Mail/.MyFolder/cur + * - TMP_DIRECTORY: ~/Mail/.MyFolder/tmp + * - CONTAINER_DIRECTORY: not used + */ + + const string getName() const; + + void createFolder(const folder::path& path); + void destroyFolder(const folder::path& path); + void renameFolder(const folder::path& oldPath, const folder::path& newPath); + + bool folderExists(const folder::path& path) const; + bool folderHasSubfolders(const folder::path& path) const; + + const utility::file::path folderPathToFileSystemPath( + const folder::path& path, + const DirectoryType type + ) const; + + const std::vector <folder::path> listFolders( + const folder::path& root, + const bool recursive + ) const; + +protected: + + bool supports() const; + + + static const string toModifiedUTF7(const folder::path::component& text); + static const folder::path::component fromModifiedUTF7(const string& text); + + void renameFolderImpl(const folder::path& oldPath, const folder::path& newPath); + + /** Test whether the specified file system directory corresponds to + * a maildir subfolder. The name of the directory should start + * with a '.' to be listed as a subfolder. + * + * @param file reference to a file system directory + * @return true if the specified directory is a maildir subfolder, + * false otherwise + */ + static bool isSubfolderDirectory(const utility::file& file); + + /** List directories corresponding to folders which are (direct or + * indirect) children of specified folder. + * + * @param root root folder + * @param dirs list in which found directories will be added + * @param onlyTestForExistence if true, the function returns as soon + * as the first directory is found + * @return true if at least one directory has been found, + * false otherwise + */ + bool listDirectories( + const folder::path& root, + std::vector <string>& dirs, + const bool onlyTestForExistence + ) const; +}; + + +} // format +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_FORMAT_COURIERMAILDIRFORMAT_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.cpp b/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.cpp new file mode 100644 index 0000000..4eb89e8 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.cpp @@ -0,0 +1,337 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/format/kmailMaildirFormat.hpp" + +#include "vmime/net/maildir/maildirStore.hpp" +#include "vmime/net/maildir/maildirUtils.hpp" + +#include "vmime/platform.hpp" + + +namespace vmime { +namespace net { +namespace maildir { +namespace format { + + +kmailMaildirFormat::kmailMaildirFormat(const shared_ptr <context>& ctx) + : maildirFormat(ctx) { + +} + + +const string kmailMaildirFormat::getName() const { + + return "kmail"; +} + + +void kmailMaildirFormat::createFolder(const folder::path& path) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + if (!fsf->isValidPath(folderPathToFileSystemPath(path, ROOT_DIRECTORY))) { + throw exceptions::invalid_folder_name(); + } + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + ); + + shared_ptr <utility::file> newDir = fsf->create( + folderPathToFileSystemPath(path, NEW_DIRECTORY) + ); + shared_ptr <utility::file> tmpDir = fsf->create( + folderPathToFileSystemPath(path, TMP_DIRECTORY) + ); + shared_ptr <utility::file> curDir = fsf->create( + folderPathToFileSystemPath(path, CUR_DIRECTORY) + ); + + rootDir->createDirectory(true); + + newDir->createDirectory(false); + tmpDir->createDirectory(false); + curDir->createDirectory(false); +} + + +void kmailMaildirFormat::destroyFolder(const folder::path& path) { + + // Delete 'folder' and '.folder.directory' directories + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + maildirUtils::recursiveFSDelete( + fsf->create(folderPathToFileSystemPath(path, ROOT_DIRECTORY)) // root + ); + + maildirUtils::recursiveFSDelete( + fsf->create(folderPathToFileSystemPath(path, CONTAINER_DIRECTORY)) // container + ); +} + + +bool kmailMaildirFormat::folderExists(const folder::path& path) const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(path, ROOT_DIRECTORY) + ); + + shared_ptr <utility::file> newDir = fsf->create( + folderPathToFileSystemPath(path, NEW_DIRECTORY) + ); + shared_ptr <utility::file> tmpDir = fsf->create( + folderPathToFileSystemPath(path, TMP_DIRECTORY) + ); + shared_ptr <utility::file> curDir = fsf->create( + folderPathToFileSystemPath(path, CUR_DIRECTORY) + ); + + return rootDir->exists() && rootDir->isDirectory() && + newDir->exists() && newDir->isDirectory() && + tmpDir->exists() && tmpDir->isDirectory() && + curDir->exists() && curDir->isDirectory(); +} + + +const utility::file::path kmailMaildirFormat::folderPathToFileSystemPath( + const folder::path& path, + const DirectoryType type +) const { + + // Root path + utility::file::path fsPath = getContext()->getStore()->getFileSystemPath(); + + const size_t pathSize = path.getSize(); + const size_t count = (type == CONTAINER_DIRECTORY + ? pathSize : (pathSize >= 1 ? pathSize - 1 : 0)); + + // Parent folders + for (size_t i = 0 ; i < count ; ++i) { + + utility::file::path::component comp(path[i]); + + // TODO: may not work with all encodings... + comp.setBuffer("." + comp.getBuffer() + ".directory"); + + fsPath /= comp; + } + + // Last component + if (path.getSize() != 0 && type != CONTAINER_DIRECTORY) { + + fsPath /= path.getLastComponent(); + + switch (type) { + + case ROOT_DIRECTORY: + + // Nothing to add + break; + + case NEW_DIRECTORY: + + fsPath /= NEW_DIR; + break; + + case CUR_DIRECTORY: + + fsPath /= CUR_DIR; + break; + + case TMP_DIRECTORY: + + fsPath /= TMP_DIR; + break; + + case CONTAINER_DIRECTORY: + + // Can't happen... + break; + } + } + + return fsPath; +} + + +const std::vector <folder::path> kmailMaildirFormat::listFolders( + const folder::path& root, + const bool recursive +) const { + + std::vector <folder::path> list; + listFoldersImpl(list, root, recursive); + + return list; +} + + +void kmailMaildirFormat::listFoldersImpl( + std::vector <folder::path>& list, + const folder::path& root, + const bool recursive +) const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(root, root.isEmpty() ? ROOT_DIRECTORY : CONTAINER_DIRECTORY) + ); + + if (rootDir->exists()) { + + shared_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) { + + shared_ptr <utility::file> file = it->nextElement(); + + if (isSubfolderDirectory(*file)) { + + const utility::path subPath = root / file->getFullPath().getLastComponent(); + + list.push_back(subPath); + + if (recursive) { + listFoldersImpl(list, subPath, true); + } + } + } + + } else { + + // No sub-folder + } +} + + +// static +bool kmailMaildirFormat::isSubfolderDirectory(const utility::file& file) { + + // A directory which name does not start with '.' is listed as a sub-folder + if (file.isDirectory() && + file.getFullPath().getLastComponent().getBuffer().length() >= 1 && + file.getFullPath().getLastComponent().getBuffer()[0] != '.') { + + return true; + } + + return false; +} + + +void kmailMaildirFormat::renameFolder(const folder::path& oldPath, const folder::path& newPath) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(oldPath, ROOT_DIRECTORY) + ); + shared_ptr <utility::file> contDir = fsf->create( + folderPathToFileSystemPath(oldPath, CONTAINER_DIRECTORY) + ); + + try { + + const utility::file::path newRootPath = + folderPathToFileSystemPath(newPath, ROOT_DIRECTORY); + const utility::file::path newContPath = + folderPathToFileSystemPath(newPath, CONTAINER_DIRECTORY); + + rootDir->rename(newRootPath); + + // Container directory may not exist, so ignore error when trying to rename it + try { + contDir->rename(newContPath); + } catch (exceptions::filesystem_exception& e) { + // Ignore + } + + } catch (exceptions::filesystem_exception& e) { + + // Revert to old location + const utility::file::path rootPath = + folderPathToFileSystemPath(oldPath, ROOT_DIRECTORY); + const utility::file::path contPath = + folderPathToFileSystemPath(oldPath, CONTAINER_DIRECTORY); + + try { + rootDir->rename(rootPath); + contDir->rename(contPath); + } catch (exceptions::filesystem_exception& e) { + // Ignore (not recoverable) + } + + throw; + } +} + + +bool kmailMaildirFormat::folderHasSubfolders(const folder::path& path) const { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> rootDir = fsf->create( + folderPathToFileSystemPath(path, CONTAINER_DIRECTORY) + ); + + shared_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) { + + shared_ptr <utility::file> file = it->nextElement(); + + if (isSubfolderDirectory(*file)) { + return true; + } + } + + return false; +} + + +bool kmailMaildirFormat::supports() const { + + // This is the default + return true; +} + + +} // format +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.hpp b/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.hpp new file mode 100644 index 0000000..26b557a --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/format/kmailMaildirFormat.hpp @@ -0,0 +1,115 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_FORMAT_KMAILMAILDIRFORMAT_HPP_INCLUDED +#define VMIME_NET_MAILDIR_FORMAT_KMAILMAILDIRFORMAT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/maildir/maildirFormat.hpp" + + +namespace vmime { +namespace net { +namespace maildir { +namespace format { + + +/** Reads KMail Maildir format. + */ +class VMIME_EXPORT kmailMaildirFormat : public maildirFormat { + +public: + + kmailMaildirFormat(const shared_ptr <context>& ctx); + + + /* Folder types: + * + * - ROOT_DIRECTORY: ~/Mail/MyFolder + * - NEW_DIRECTORY: ~/Mail/MyFolder/new + * - CUR_DIRECTORY: ~/Mail/MyFolder/cur + * - TMP_DIRECTORY: ~/Mail/MyFolder/tmp + * - CONTAINER_DIRECTORY: ~/Mail/.MyFolder.directory + */ + + const string getName() const; + + void createFolder(const folder::path& path); + void destroyFolder(const folder::path& path); + void renameFolder(const folder::path& oldPath, const folder::path& newPath); + + bool folderExists(const folder::path& path) const; + bool folderHasSubfolders(const folder::path& path) const; + + const utility::file::path folderPathToFileSystemPath( + const folder::path& path, + const DirectoryType type + ) const; + + const std::vector <folder::path> listFolders( + const folder::path& root, + const bool recursive + ) const; + +protected: + + bool supports() const; + + + /** Recursive implementation of listFolders(). + */ + void listFoldersImpl( + std::vector <folder::path>& list, + const folder::path& root, + const bool recursive + ) const; + + /** Test whether the specified file system directory corresponds to + * a maildir subfolder. The name of the directory should not start + * with '.' to be listed as a subfolder. + * + * @param file reference to a file system directory + * @return true if the specified directory is a maildir subfolder, + * false otherwise + */ + static bool isSubfolderDirectory(const utility::file& file); +}; + + +} // format +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#endif // VMIME_NET_MAILDIR_FORMAT_KMAILMAILDIRFORMAT_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/maildir/maildir.hpp b/vmime-master/src/vmime/net/maildir/maildir.hpp new file mode 100644 index 0000000..8835bf4 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildir.hpp @@ -0,0 +1,34 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIR_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIR_HPP_INCLUDED + + +#include "vmime/net/maildir/maildirFolder.hpp" +#include "vmime/net/maildir/maildirFolderStatus.hpp" +#include "vmime/net/maildir/maildirMessage.hpp" +#include "vmime/net/maildir/maildirStore.hpp" + + +#endif // VMIME_NET_MAILDIR_MAILDIR_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirFolder.cpp b/vmime-master/src/vmime/net/maildir/maildirFolder.cpp new file mode 100644 index 0000000..8c02025 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFolder.cpp @@ -0,0 +1,1365 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirFolder.hpp" + +#include "vmime/net/maildir/maildirStore.hpp" +#include "vmime/net/maildir/maildirMessage.hpp" +#include "vmime/net/maildir/maildirUtils.hpp" +#include "vmime/net/maildir/maildirFormat.hpp" +#include "vmime/net/maildir/maildirFolderStatus.hpp" + +#include "vmime/message.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirFolder::maildirFolder( + const folder::path& path, + const shared_ptr <maildirStore>& store +) + : m_store(store), + m_path(path), + m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), + m_mode(-1), + m_open(false), + m_unreadMessageCount(0), + m_messageCount(0) { + + store->registerFolder(this); +} + + +maildirFolder::~maildirFolder() { + + try { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (store) { + + if (m_open) { + close(false); + } + + store->unregisterFolder(this); + + } else if (m_open) { + + close(false); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void maildirFolder::onStoreDisconnected() { + + m_store.reset(); +} + + +int maildirFolder::getMode() const { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_mode; +} + + +const folderAttributes maildirFolder::getAttributes() { + + folderAttributes attribs; + + if (m_path.isEmpty()) { + attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS); + } else { + attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS | folderAttributes::TYPE_CONTAINS_MESSAGES); + } + + if (m_store.lock()->getFormat()->folderHasSubfolders(m_path)) { + attribs.setFlags(folderAttributes::FLAG_HAS_CHILDREN); // contains at least one sub-folder + } + + return attribs; +} + + +const folder::path::component maildirFolder::getName() const { + + return m_name; +} + + +const folder::path maildirFolder::getFullPath() const { + + return m_path; +} + + +void maildirFolder::open(const int mode, bool /* failIfModeIsNotAvailable */) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (isOpen()) { + throw exceptions::illegal_state("Folder is already open"); + } else if (!exists()) { + throw exceptions::illegal_state("Folder does not exist"); + } + + scanFolder(); + + m_open = true; + m_mode = mode; +} + + +void maildirFolder::close(const bool expunge) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (expunge) { + this->expunge(); + } + + m_open = false; + m_mode = -1; + + onClose(); +} + + +void maildirFolder::onClose() { + + for (std::vector <maildirMessage*>::iterator it = m_messages.begin() ; + it != m_messages.end() ; ++it) { + + (*it)->onFolderClosed(); + } + + m_messages.clear(); +} + + +void maildirFolder::registerMessage(maildirMessage* msg) { + + m_messages.push_back(msg); +} + + +void maildirFolder::unregisterMessage(maildirMessage* msg) { + + std::vector <maildirMessage*>::iterator it = + std::find(m_messages.begin(), m_messages.end(), msg); + + if (it != m_messages.end()) { + m_messages.erase(it); + } +} + + +void maildirFolder::create(const folderAttributes& /* attribs */) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (isOpen()) { + throw exceptions::illegal_state("Folder is open"); + } else if (exists()) { + throw exceptions::illegal_state("Folder already exists"); + } else if (!store->isValidFolderName(m_name)) { + throw exceptions::invalid_folder_name(); + } + + // Create directory on file system + try { + store->getFormat()->createFolder(m_path); + } catch (exceptions::filesystem_exception& e) { + throw exceptions::command_error("CREATE", "", "File system exception", e); + } + + // Notify folder created + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_CREATED, + m_path, m_path + ); + + notifyFolder(event); +} + + +void maildirFolder::destroy() { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (isOpen()) { + throw exceptions::illegal_state("Folder is open"); + } + + // Delete folder + try { + store->getFormat()->destroyFolder(m_path); + } catch (std::exception&) { + // Ignore exception: anyway, we can't recover from this... + } + + // Notify folder deleted + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_DELETED, + m_path, m_path + ); + + notifyFolder(event); +} + + +bool maildirFolder::exists() { + + shared_ptr <maildirStore> store = m_store.lock(); + + return store->getFormat()->folderExists(m_path); +} + + +bool maildirFolder::isOpen() const { + + return m_open; +} + + +void maildirFolder::scanFolder() { + + shared_ptr <maildirStore> store = m_store.lock(); + + try { + + m_messageCount = 0; + m_unreadMessageCount = 0; + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + utility::file::path newDirPath = + store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::NEW_DIRECTORY); + shared_ptr <utility::file> newDir = fsf->create(newDirPath); + + utility::file::path curDirPath = + store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY); + shared_ptr <utility::file> curDir = fsf->create(curDirPath); + + // New received messages (new/) + shared_ptr <utility::fileIterator> nit = newDir->getFiles(); + std::vector <utility::file::path::component> newMessageFilenames; + + while (nit->hasMoreElements()) { + + shared_ptr <utility::file> file = nit->nextElement(); + + if (maildirUtils::isMessageFile(*file)) { + newMessageFilenames.push_back(file->getFullPath().getLastComponent()); + } + } + + // Current messages (cur/) + shared_ptr <utility::fileIterator> cit = curDir->getFiles(); + std::vector <utility::file::path::component> curMessageFilenames; + + while (cit->hasMoreElements()) { + + shared_ptr <utility::file> file = cit->nextElement(); + + if (maildirUtils::isMessageFile(*file)) { + curMessageFilenames.push_back(file->getFullPath().getLastComponent()); + } + } + + // Update/delete existing messages (found in previous scan) + for (size_t i = 0 ; i < m_messageInfos.size() ; ++i) { + + messageInfos& msgInfos = m_messageInfos[i]; + + // NOTE: the flags may have changed (eg. moving from 'new' to 'cur' + // may imply the 'S' flag) and so the filename. That's why we use + // "maildirUtils::messageIdComparator" to compare only the 'unique' + // portion of the filename... + + if (msgInfos.type == messageInfos::TYPE_CUR) { + + const std::vector <utility::file::path::component>::iterator pos = + std::find_if( + curMessageFilenames.begin(), + curMessageFilenames.end(), + maildirUtils::messageIdComparator(msgInfos.path) + ); + + // If we cannot find this message in the 'cur' directory, + // it means it has been deleted (and expunged). + if (pos == curMessageFilenames.end()) { + + msgInfos.type = messageInfos::TYPE_DELETED; + + // Otherwise, update its information. + } else { + + msgInfos.path = *pos; + curMessageFilenames.erase(pos); + } + } + } + + m_messageInfos.reserve(m_messageInfos.size() + + newMessageFilenames.size() + curMessageFilenames.size()); + + // Add new messages from 'new': we are responsible to move the files + // from the 'new' directory to the 'cur' directory, and append them + // to our message list. + for (std::vector <utility::file::path::component>::const_iterator + it = newMessageFilenames.begin() ; it != newMessageFilenames.end() ; ++it) { + + const utility::file::path::component newFilename = + maildirUtils::buildFilename(maildirUtils::extractId(*it), 0); + + // Move messages from 'new' to 'cur' + shared_ptr <utility::file> file = fsf->create(newDirPath / *it); + file->rename(curDirPath / newFilename); + + // Append to message list + messageInfos msgInfos; + msgInfos.path = newFilename; + + if (maildirUtils::extractFlags(msgInfos.path) & message::FLAG_DELETED) { + msgInfos.type = messageInfos::TYPE_DELETED; + } else { + msgInfos.type = messageInfos::TYPE_CUR; + } + + m_messageInfos.push_back(msgInfos); + } + + // Add new messages from 'cur': the files have already been moved + // from 'new' to 'cur'. Just append them to our message list. + for (std::vector <utility::file::path::component>::const_iterator + it = curMessageFilenames.begin() ; it != curMessageFilenames.end() ; ++it) { + + // Append to message list + messageInfos msgInfos; + msgInfos.path = *it; + + if (maildirUtils::extractFlags(msgInfos.path) & message::FLAG_DELETED) { + msgInfos.type = messageInfos::TYPE_DELETED; + } else { + msgInfos.type = messageInfos::TYPE_CUR; + } + + m_messageInfos.push_back(msgInfos); + } + + // Update message count + size_t unreadMessageCount = 0; + + for (std::vector <messageInfos>::const_iterator + it = m_messageInfos.begin() ; it != m_messageInfos.end() ; ++it) { + + if ((maildirUtils::extractFlags((*it).path) & message::FLAG_SEEN) == 0) { + ++unreadMessageCount; + } + } + + m_unreadMessageCount = unreadMessageCount; + m_messageCount = static_cast <size_t>(m_messageInfos.size()); + + } catch (exceptions::filesystem_exception&) { + + // Should not happen... + } +} + + +shared_ptr <message> maildirFolder::getMessage(const size_t num) { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (num < 1 || num > m_messageCount) { + throw exceptions::message_not_found(); + } + + return make_shared <maildirMessage>(dynamicCast <maildirFolder>(shared_from_this()), num); +} + + +std::vector <shared_ptr <message> > maildirFolder::getMessages(const messageSet& msgs) { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msgs.isNumberSet()) { + + const std::vector <size_t> numbers = maildirUtils::messageSetToNumberList(msgs, m_messageCount); + + std::vector <shared_ptr <message> > messages; + shared_ptr <maildirFolder> thisFolder = dynamicCast <maildirFolder>(shared_from_this()); + + for (std::vector <size_t>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it) { + + if (*it < 1|| *it > m_messageCount) { + throw exceptions::message_not_found(); + } + + messages.push_back(make_shared <maildirMessage>(thisFolder, *it)); + } + + return messages; + + } else { + + throw exceptions::operation_not_supported(); + } +} + + +size_t maildirFolder::getMessageCount() { + + return m_messageCount; +} + + +shared_ptr <folder> maildirFolder::getFolder(const folder::path::component& name) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + return shared_ptr <maildirFolder>(new maildirFolder(m_path / name, store)); +} + + +std::vector <shared_ptr <folder> > maildirFolder::getFolders(const bool recursive) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!isOpen() && !store) { + throw exceptions::illegal_state("Store disconnected"); + } + + std::vector <shared_ptr <folder> > list; + + listFolders(list, recursive); + + return list; +} + + +void maildirFolder::listFolders(std::vector <shared_ptr <folder> >& list, const bool recursive) { + + shared_ptr <maildirStore> store = m_store.lock(); + + try { + + std::vector <folder::path> pathList = + store->getFormat()->listFolders(m_path, recursive); + + list.reserve(pathList.size()); + + for (std::vector <folder::path>::size_type i = 0, n = pathList.size() ; i < n ; ++i) { + + shared_ptr <maildirFolder> subFolder( + new maildirFolder(pathList[i], store) + ); + + list.push_back(subFolder); + } + + } catch (exceptions::filesystem_exception& e) { + + throw exceptions::command_error("LIST", "", "", e); + } +} + + +void maildirFolder::rename(const folder::path& newPath) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (m_path.isEmpty() || newPath.isEmpty()) { + throw exceptions::illegal_operation("Cannot rename root folder"); + } else if (!store->isValidFolderName(newPath.getLastComponent())) { + throw exceptions::invalid_folder_name(); + } + + // Rename the directory on the file system + try { + store->getFormat()->renameFolder(m_path, newPath); + } catch (vmime::exception& e) { + throw exceptions::command_error("RENAME", "", "", e); + } + + // Notify folder renamed + folder::path oldPath(m_path); + + m_path = newPath; + m_name = newPath.getLastComponent(); + + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>(shared_from_this()), + events::folderEvent::TYPE_RENAMED, + oldPath, newPath + ); + + notifyFolder(event); + + // Notify folders with the same path + for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == oldPath) { + + (*it)->m_path = newPath; + (*it)->m_name = newPath.getLastComponent(); + + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::folderEvent::TYPE_RENAMED, + oldPath, newPath + ); + + (*it)->notifyFolder(event); + + } else if ((*it) != this && oldPath.isParentOf((*it)->getFullPath())) { + + folder::path oldPath((*it)->m_path); + + (*it)->m_path.renameParent(oldPath, newPath); + + shared_ptr <events::folderEvent> event = + make_shared <events::folderEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::folderEvent::TYPE_RENAMED, + oldPath, (*it)->m_path + ); + + (*it)->notifyFolder(event); + } + } +} + + +void maildirFolder::deleteMessages(const messageSet& msgs) { + + // Mark messages as deleted + setMessageFlags(msgs, message::FLAG_DELETED, message::FLAG_MODE_ADD); +} + + +void maildirFolder::setMessageFlags( + const messageSet& msgs, + const int flags, + const int mode +) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + if (msgs.isNumberSet()) { + + const std::vector <size_t> nums = maildirUtils::messageSetToNumberList(msgs, m_messageCount); + + // Change message flags + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + utility::file::path curDirPath = store->getFormat()-> + folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY); + + for (std::vector <size_t>::const_iterator it = + nums.begin() ; it != nums.end() ; ++it) { + + const size_t num = *it - 1; + + try { + + const utility::file::path::component path = m_messageInfos[num].path; + shared_ptr <utility::file> file = fsf->create(curDirPath / path); + + int newFlags = maildirUtils::extractFlags(path); + + switch (mode) { + case message::FLAG_MODE_ADD: newFlags |= flags; break; + case message::FLAG_MODE_REMOVE: newFlags &= ~flags; break; + default: + case message::FLAG_MODE_SET: newFlags = flags; break; + } + + const utility::file::path::component newPath = + maildirUtils::buildFilename(maildirUtils::extractId(path), newFlags); + + file->rename(curDirPath / newPath); + + if (flags & message::FLAG_DELETED) { + m_messageInfos[num].type = messageInfos::TYPE_DELETED; + } else { + m_messageInfos[num].type = messageInfos::TYPE_CUR; + } + + m_messageInfos[num].path = newPath; + + } catch (exceptions::filesystem_exception& e) { + + // Ignore (not important) + } + } + + // Update local flags + switch (mode) { + + case message::FLAG_MODE_ADD: { + + for (std::vector <maildirMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) { + + if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) && + (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) { + + (*it)->m_flags |= flags; + } + } + + break; + } + case message::FLAG_MODE_REMOVE: { + + for (std::vector <maildirMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) { + + if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) && + (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) { + + (*it)->m_flags &= ~flags; + } + } + + break; + } + default: + case message::FLAG_MODE_SET: { + + for (std::vector <maildirMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) { + + if (std::binary_search(nums.begin(), nums.end(), (*it)->getNumber()) && + (*it)->m_flags != maildirMessage::FLAG_UNDEFINED) { + + (*it)->m_flags = flags; + } + } + + break; + } + + } + + // Notify message flags changed + shared_ptr <events::messageChangedEvent> event = + make_shared <events::messageChangedEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageChangedEvent::TYPE_FLAGS, + nums + ); + + notifyMessageChanged(event); + + // TODO: notify other folders with the same path + + } else { + + throw exceptions::operation_not_supported(); + } +} + + +messageSet maildirFolder::addMessage( + const shared_ptr <vmime::message>& msg, + const int flags, + vmime::datetime* date, + utility::progressListener* progress +) { + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const string& str = oss.str(); + utility::inputStreamStringAdapter strAdapter(str); + + return addMessage(strAdapter, str.length(), flags, date, progress); +} + + +messageSet maildirFolder::addMessage( + utility::inputStream& is, + const size_t size, + const int flags, + vmime::datetime* /* date */, + utility::progressListener* progress +) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + utility::file::path tmpDirPath = store->getFormat()-> + folderPathToFileSystemPath(m_path,maildirFormat::TMP_DIRECTORY); + utility::file::path dstDirPath = store->getFormat()-> + folderPathToFileSystemPath( + m_path, + flags == message::FLAG_RECENT ? + maildirFormat::NEW_DIRECTORY : + maildirFormat::CUR_DIRECTORY + ); + + const utility::file::path::component filename = + maildirUtils::buildFilename(maildirUtils::generateId(), ((flags == -1) ? 0 : flags)); + + try { + shared_ptr <utility::file> tmpDir = fsf->create(tmpDirPath); + tmpDir->createDirectory(true); + } catch (exceptions::filesystem_exception&) { + // Don't throw now, it will fail later... + } + + try { + shared_ptr <utility::file> curDir = fsf->create(dstDirPath); + curDir->createDirectory(true); + } catch (exceptions::filesystem_exception&) { + // Don't throw now, it will fail later... + } + + // Actually add the message + copyMessageImpl(tmpDirPath, dstDirPath, filename, is, size, progress); + + // Append the message to the cache list + messageInfos msgInfos; + msgInfos.path = filename; + msgInfos.type = messageInfos::TYPE_CUR; + + m_messageInfos.push_back(msgInfos); + m_messageCount++; + + if ((flags == -1) || !(flags & message::FLAG_SEEN)) { + m_unreadMessageCount++; + } + + // Notification + std::vector <size_t> nums; + nums.push_back(m_messageCount); + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + notifyMessageCount(event); + + // Notify folders with the same path + for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == m_path) { + + (*it)->m_messageCount = m_messageCount; + (*it)->m_unreadMessageCount = m_unreadMessageCount; + + (*it)->m_messageInfos.resize(m_messageInfos.size()); + std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + (*it)->notifyMessageCount(event); + } + } + + return messageSet::empty(); +} + + +void maildirFolder::copyMessageImpl( + const utility::file::path& tmpDirPath, + const utility::file::path& dstDirPath, + const utility::file::path::component& filename, + utility::inputStream& is, const size_t size, + utility::progressListener* progress +) { + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + shared_ptr <utility::file> file = fsf->create(tmpDirPath / filename); + + if (progress) { + progress->start(size); + } + + // First, write the message into 'tmp'... + try { + + file->createFile(); + + shared_ptr <utility::fileWriter> fw = file->getFileWriter(); + shared_ptr <utility::outputStream> os = fw->getOutputStream(); + + byte_t buffer[65536]; + size_t total = 0; + + while (!is.eof()) { + + const size_t read = is.read(buffer, sizeof(buffer)); + + if (read != 0) { + os->write(buffer, read); + total += read; + } + + if (progress) { + progress->progress(total, size); + } + } + + os->flush(); + + } catch (exception& e) { + + if (progress) { + progress->stop(size); + } + + // Delete temporary file + try { + shared_ptr <utility::file> file = fsf->create(tmpDirPath / filename); + file->remove(); + } catch (exceptions::filesystem_exception&) { + // Ignore + } + + throw exceptions::command_error("ADD", "", "", e); + } + + // ...then, move it to 'cur' + try { + + file->rename(dstDirPath / filename); + + } catch (exception& e) { + + if (progress) { + progress->stop(size); + } + + // Delete temporary file + try { + + file->remove(); + shared_ptr <utility::file> file = fsf->create(dstDirPath / filename); + file->remove(); + + } catch (exceptions::filesystem_exception&) { + + // Ignore + } + + throw exceptions::command_error("ADD", "", "", e); + } + + if (progress) { + progress->stop(size); + } +} + + +messageSet maildirFolder::copyMessages(const folder::path& dest, const messageSet& msgs) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + utility::file::path curDirPath = + store->getFormat()->folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY); + + utility::file::path destCurDirPath = store->getFormat()-> + folderPathToFileSystemPath(dest, maildirFormat::CUR_DIRECTORY); + utility::file::path destTmpDirPath = store->getFormat()-> + folderPathToFileSystemPath(dest, maildirFormat::TMP_DIRECTORY); + + // Create destination directories + try { + + shared_ptr <utility::file> destTmpDir = fsf->create(destTmpDirPath); + destTmpDir->createDirectory(true); + + } catch (exceptions::filesystem_exception&) { + + // Don't throw now, it will fail later... + } + + try { + + shared_ptr <utility::file> destCurDir = fsf->create(destCurDirPath); + destCurDir->createDirectory(true); + + } catch (exceptions::filesystem_exception&) { + + // Don't throw now, it will fail later... + } + + // Copy messages + const std::vector <size_t> nums = maildirUtils::messageSetToNumberList(msgs, m_messageCount); + + try { + + for (std::vector <size_t>::const_iterator it = + nums.begin() ; it != nums.end() ; ++it) { + + const size_t num = *it; + const messageInfos& msg = m_messageInfos[num - 1]; + const int flags = maildirUtils::extractFlags(msg.path); + + const utility::file::path::component filename = + maildirUtils::buildFilename(maildirUtils::generateId(), flags); + + shared_ptr <utility::file> file = fsf->create(curDirPath / msg.path); + shared_ptr <utility::fileReader> fr = file->getFileReader(); + shared_ptr <utility::inputStream> is = fr->getInputStream(); + + copyMessageImpl(destTmpDirPath, destCurDirPath, + filename, *is, file->getLength(), NULL); + } + + } catch (exception& e) { + + notifyMessagesCopied(dest); + throw exceptions::command_error("COPY", "", "", e); + } + + notifyMessagesCopied(dest); + + return messageSet::empty(); +} + + +void maildirFolder::notifyMessagesCopied(const folder::path& dest) { + + shared_ptr <maildirStore> store = m_store.lock(); + + for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == dest) { + + // We only need to update the first folder we found as calling + // status() will notify all the folders with the same path. + size_t count, unseen; + (*it)->status(count, unseen); + + return; + } + } +} + + +void maildirFolder::status(size_t& count, size_t& unseen) { + + count = 0; + unseen = 0; + + shared_ptr <folderStatus> status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); + + m_messageCount = count; +} + + +shared_ptr <folderStatus> maildirFolder::getStatus() { + + shared_ptr <maildirStore> store = m_store.lock(); + + const size_t oldCount = m_messageCount; + + scanFolder(); + + shared_ptr <maildirFolderStatus> status = make_shared <maildirFolderStatus>(); + + status->setMessageCount(m_messageCount); + status->setUnseenCount(m_unreadMessageCount); + + // Notify message count changed (new messages) + if (m_messageCount > oldCount) { + + std::vector <size_t> nums; + nums.reserve(m_messageCount - oldCount); + + for (size_t i = oldCount + 1, j = 0 ; i <= m_messageCount ; ++i, ++j) { + nums[j] = i; + } + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + notifyMessageCount(event); + + // Notify folders with the same path + for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == m_path) { + + (*it)->m_messageCount = m_messageCount; + (*it)->m_unreadMessageCount = m_unreadMessageCount; + + (*it)->m_messageInfos.resize(m_messageInfos.size()); + std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + (*it)->notifyMessageCount(event); + } + } + } + + return status; +} + + +void maildirFolder::expunge() { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (m_mode == MODE_READ_ONLY) { + throw exceptions::illegal_state("Folder is read-only"); + } + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + utility::file::path curDirPath = store->getFormat()-> + folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY); + + std::vector <size_t> nums; + size_t unreadCount = 0; + + for (size_t num = 1 ; num <= m_messageCount ; ++num) { + + messageInfos& infos = m_messageInfos[num - 1]; + + if (infos.type == messageInfos::TYPE_DELETED) { + + nums.push_back(num); + + for (std::vector <maildirMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) { + + if ((*it)->m_num == num) { + (*it)->m_expunged = true; + } else if ((*it)->m_num > num) { + (*it)->m_num--; + } + } + + if (maildirUtils::extractFlags(infos.path) & message::FLAG_SEEN) { + ++unreadCount; + } + + // Delete file from file system + try { + shared_ptr <utility::file> file = fsf->create(curDirPath / infos.path); + file->remove(); + } catch (exceptions::filesystem_exception& e) { + // Ignore (not important) + } + } + } + + if (!nums.empty()) { + + for (std::vector <size_t>::size_type i = nums.size() ; i != 0 ; --i) { + m_messageInfos.erase(m_messageInfos.begin() + (i - 1)); + } + } + + m_messageCount -= static_cast <size_t>(nums.size()); + m_unreadMessageCount -= unreadCount; + + // Notify message expunged + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_REMOVED, + nums + ); + + notifyMessageCount(event); + + // Notify folders with the same path + for (std::list <maildirFolder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == m_path) { + + (*it)->m_messageCount = m_messageCount; + (*it)->m_unreadMessageCount = m_unreadMessageCount; + + (*it)->m_messageInfos.resize(m_messageInfos.size()); + std::copy(m_messageInfos.begin(), m_messageInfos.end(), (*it)->m_messageInfos.begin()); + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::messageCountEvent::TYPE_REMOVED, + nums + ); + + (*it)->notifyMessageCount(event); + } + } +} + + +shared_ptr <folder> maildirFolder::getParent() { + + if (m_path.isEmpty()) { + return null; + } else { + return shared_ptr <maildirFolder>(new maildirFolder(m_path.getParent(), m_store.lock())); + } +} + + +shared_ptr <const store> maildirFolder::getStore() const { + + return m_store.lock(); +} + + +shared_ptr <store> maildirFolder::getStore() { + + return m_store.lock(); +} + + +void maildirFolder::fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress +) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msg.empty()) { + return; + } + + const size_t total = msg.size(); + size_t current = 0; + + if (progress) { + progress->start(total); + } + + shared_ptr <maildirFolder> thisFolder = dynamicCast <maildirFolder>(shared_from_this()); + + for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; + it != msg.end() ; ++it) { + + dynamicCast <maildirMessage>(*it)->fetch(thisFolder, options); + + if (progress) { + progress->progress(++current, total); + } + } + + if (progress) { + progress->stop(total); + } +} + + +void maildirFolder::fetchMessage( + const shared_ptr <message>& msg, + const fetchAttributes& options +) { + + shared_ptr <maildirStore> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + dynamicCast <maildirMessage>(msg)->fetch( + dynamicCast <maildirFolder>(shared_from_this()), + options + ); +} + + +std::vector <shared_ptr <message> > maildirFolder::getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs +) { + + if (msgs.isEmpty()) { + return std::vector <shared_ptr <message> >(); + } + + std::vector <shared_ptr <message> > messages = getMessages(msgs); + fetchMessages(messages, attribs); + + return messages; +} + + +int maildirFolder::getFetchCapabilities() const { + + return fetchAttributes::ENVELOPE | + fetchAttributes::STRUCTURE | + fetchAttributes::CONTENT_INFO | + fetchAttributes::FLAGS | + fetchAttributes::SIZE | + fetchAttributes::FULL_HEADER | + fetchAttributes::UID | + fetchAttributes::IMPORTANCE; +} + + +const utility::file::path maildirFolder::getMessageFSPath(const size_t number) const { + + utility::file::path curDirPath = m_store.lock()->getFormat()-> + folderPathToFileSystemPath(m_path, maildirFormat::CUR_DIRECTORY); + + return curDirPath / m_messageInfos[number - 1].path; +} + + +std::vector <size_t> maildirFolder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) { + + throw exceptions::operation_not_supported(); +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirFolder.hpp b/vmime-master/src/vmime/net/maildir/maildirFolder.hpp new file mode 100644 index 0000000..24f2bf8 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFolder.hpp @@ -0,0 +1,211 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include <vector> +#include <map> + +#include "vmime/types.hpp" + +#include "vmime/net/folder.hpp" + +#include "vmime/utility/file.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirStore; +class maildirMessage; + + +/** maildir folder implementation. + */ +class VMIME_EXPORT maildirFolder : public folder { + +private: + + friend class maildirStore; + friend class maildirMessage; + + maildirFolder(const maildirFolder&) : folder() { } + maildirFolder(const folder::path& path, const shared_ptr <maildirStore>& store); + +public: + + ~maildirFolder(); + + + int getMode() const; + + const folderAttributes getAttributes(); + + const folder::path::component getName() const; + const folder::path getFullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const folderAttributes& attribs); + + bool exists(); + + void destroy(); + + bool isOpen() const; + + shared_ptr <message> getMessage(const size_t num); + std::vector <shared_ptr <message> > getMessages(const messageSet& msgs); + + size_t getMessageCount(); + + shared_ptr <folder> getFolder(const folder::path::component& name); + std::vector <shared_ptr <folder> > getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessages(const messageSet& msgs); + + void setMessageFlags( + const messageSet& msgs, + const int flags, + const int mode = message::FLAG_MODE_SET + ); + + messageSet addMessage( + const shared_ptr <vmime::message>& msg, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet addMessage( + utility::inputStream& is, + const size_t size, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet copyMessages(const folder::path& dest, const messageSet& msgs); + + void status(size_t& count, size_t& unseen); + shared_ptr <folderStatus> getStatus(); + + void expunge(); + + shared_ptr <folder> getParent(); + + shared_ptr <const store> getStore() const; + shared_ptr <store> getStore(); + + + void fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress = NULL + ); + + void fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options); + + std::vector <shared_ptr <message> > getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs + ); + + int getFetchCapabilities() const; + + std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); + +private: + + void scanFolder(); + + void listFolders(std::vector <shared_ptr <folder> >& list, const bool recursive); + + void registerMessage(maildirMessage* msg); + void unregisterMessage(maildirMessage* msg); + + const utility::file::path getMessageFSPath(const size_t number) const; + + void onStoreDisconnected(); + + void onClose(); + + void deleteMessagesImpl(const std::vector <size_t>& nums); + void setMessageFlagsImpl(const std::vector <size_t>& nums, const int flags, const int mode); + + void copyMessagesImpl(const folder::path& dest, const std::vector <size_t>& nums); + void copyMessageImpl(const utility::file::path& tmpDirPath, const utility::file::path& curDirPath, const utility::file::path::component& filename, utility::inputStream& is, const size_t size, utility::progressListener* progress); + + void notifyMessagesCopied(const folder::path& dest); + + + weak_ptr <maildirStore> m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + size_t m_unreadMessageCount; + size_t m_messageCount; + + // Store information about scanned messages + struct messageInfos { + + enum Type { + TYPE_CUR, + TYPE_DELETED + }; + + utility::file::path::component path; // filename + Type type; // current location + }; + + std::vector <messageInfos> m_messageInfos; + + // Instanciated message objects + std::vector <maildirMessage*> m_messages; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirFolderStatus.cpp b/vmime-master/src/vmime/net/maildir/maildirFolderStatus.cpp new file mode 100644 index 0000000..7438d8c --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFolderStatus.cpp @@ -0,0 +1,88 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirFolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirFolderStatus::maildirFolderStatus() + : m_count(0), + m_unseen(0) { + +} + + +maildirFolderStatus::maildirFolderStatus(const maildirFolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen) { + +} + + +size_t maildirFolderStatus::getMessageCount() const { + + return m_count; +} + + +size_t maildirFolderStatus::getUnseenCount() const { + + return m_unseen; +} + + +void maildirFolderStatus::setMessageCount(const size_t count) { + + m_count = count; +} + + +void maildirFolderStatus::setUnseenCount(const size_t unseen) { + + m_unseen = unseen; +} + + +shared_ptr <folderStatus> maildirFolderStatus::clone() const { + + return make_shared <maildirFolderStatus>(*this); +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR diff --git a/vmime-master/src/vmime/net/maildir/maildirFolderStatus.hpp b/vmime-master/src/vmime/net/maildir/maildirFolderStatus.hpp new file mode 100644 index 0000000..3b69375 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFolderStatus.hpp @@ -0,0 +1,75 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/folderStatus.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +/** Holds the status of a Maildir folder. + */ +class VMIME_EXPORT maildirFolderStatus : public folderStatus { + +public: + + maildirFolderStatus(); + maildirFolderStatus(const maildirFolderStatus& other); + + // Inherited from folderStatus + size_t getMessageCount() const; + size_t getUnseenCount() const; + + shared_ptr <folderStatus> clone() const; + + + void setMessageCount(const size_t count); + void setUnseenCount(const size_t unseen); + +private: + + size_t m_count; + size_t m_unseen; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRFOLDERSTATUS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirFormat.cpp b/vmime-master/src/vmime/net/maildir/maildirFormat.cpp new file mode 100644 index 0000000..914c078 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFormat.cpp @@ -0,0 +1,104 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirFormat.hpp" +#include "vmime/net/maildir/maildirStore.hpp" + +#include "vmime/net/maildir/format/kmailMaildirFormat.hpp" +#include "vmime/net/maildir/format/courierMaildirFormat.hpp" + +#include "vmime/utility/file.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +const utility::file::path::component maildirFormat::TMP_DIR("tmp", vmime::charset(vmime::charsets::US_ASCII)); +const utility::file::path::component maildirFormat::CUR_DIR("cur", vmime::charset(vmime::charsets::US_ASCII)); +const utility::file::path::component maildirFormat::NEW_DIR("new", vmime::charset(vmime::charsets::US_ASCII)); + + +// +// maildirFormat::context +// + +maildirFormat::context::context(const shared_ptr <maildirStore>& store) + : m_store(store) { + +} + + +shared_ptr <maildirStore> maildirFormat::context::getStore() { + + return m_store.lock(); +} + + +// +// maildirFormat +// + +maildirFormat::maildirFormat(const shared_ptr <context>& ctx) + : m_context(ctx) { + +} + + +shared_ptr <maildirFormat::context> maildirFormat::getContext() const { + + return m_context; +} + + +// static +shared_ptr <maildirFormat> maildirFormat::detect(const shared_ptr <maildirStore>& store) { + + shared_ptr <context> ctx = make_shared <context>(store); + + // Try Courier format + shared_ptr <maildirFormat> fmt = make_shared <format::courierMaildirFormat>(ctx); + + if (fmt->supports()) { + return fmt; + } + + // Default is KMail format + return make_shared <format::kmailMaildirFormat>(ctx); +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirFormat.hpp b/vmime-master/src/vmime/net/maildir/maildirFormat.hpp new file mode 100644 index 0000000..9b9e063 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirFormat.hpp @@ -0,0 +1,192 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_FORMAT_MAILDIRFORMAT_HPP_INCLUDED +#define VMIME_NET_MAILDIR_FORMAT_MAILDIRFORMAT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/folder.hpp" + +#include "vmime/utility/file.hpp" +#include "vmime/utility/path.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirStore; + + +/** Interface for an object capable of reading a specific Maildir format. + */ +class VMIME_EXPORT maildirFormat : public object { + +public: + + class context : public object { + + public: + + context(const shared_ptr <maildirStore>& store); + + shared_ptr <maildirStore> getStore(); + + private: + + weak_ptr <maildirStore> m_store; + }; + + + /** Physical directory types. */ + enum DirectoryType { + ROOT_DIRECTORY, /**< Root directory. */ + NEW_DIRECTORY, /**< Directory containing unread messages. */ + CUR_DIRECTORY, /**< Directory containing messages that have been seen. */ + TMP_DIRECTORY, /**< Temporary directory used for reliable delivery. */ + CONTAINER_DIRECTORY /**< Container for subfolders. */ + }; + + /** Return the name of this Maildir format. + * + * @return format name + */ + virtual const string getName() const = 0; + + /** Create the specified folder. + * + * @param path virtual path of the folder + * @throw exceptions::filesystem_exception, invalid_folder_name + */ + virtual void createFolder(const folder::path& path) = 0; + + /** Destroy the specified folder. + * + * @param path virtual path of the folder + * @throw exceptions::filesystem_exception + */ + virtual void destroyFolder(const folder::path& path) = 0; + + /** Rename the specified folder. + * + * @param oldPath old virtual path of the folder + * @param newPath new virtual path of the folder + * @throw exceptions::filesystem_exception + */ + virtual void renameFolder(const folder::path& oldPath, const folder::path& newPath) = 0; + + /** Test whether the specified folder exists. + * + * @param path virtual path of the folder + * @return true if the folder exists, false otherwise + */ + virtual bool folderExists(const folder::path& path) const = 0; + + /** Test whether the specified folder has subfolders. + * + * @param path virtual path of the folder + * @return true if the folder has at least one subfolder, + * false otherwise + */ + virtual bool folderHasSubfolders(const folder::path& path) const = 0; + + /** Returns the directory which represents the specified + * folder on the file system. + * + * @param path virtual path of the folder + * @param type type of directory to return + * @return corresponding directory on the file system + */ + virtual const utility::file::path folderPathToFileSystemPath( + const folder::path& path, + const DirectoryType type + ) const = 0; + + /** List subfolders in the specified folder. + * + * @param root root folder in which to start the search + * @param recursive if set to true, all the descendant are + * returned; if set to false, only direct children are returned. + * @return list of subfolders + */ + virtual const std::vector <folder::path> listFolders( + const folder::path& root, + const bool recursive + ) const = 0; + + + /** Try to detect the format of the specified Maildir store. + * If the format cannot be detected, a compatible implementation + * will be returned. + * + * @param store of which to detect format + * @return a Maildir format implementation for the specified store + */ + static shared_ptr <maildirFormat> detect(const shared_ptr <maildirStore>& store); + +protected: + + static const utility::file::path::component TMP_DIR; /**< Ensure reliable delivery (not to be listed). */ + static const utility::file::path::component CUR_DIR; /**< No longer new messages. */ + static const utility::file::path::component NEW_DIR; /**< Unread messages. */ + + + maildirFormat(const shared_ptr <context>& ctx); + + + /** Returns the current context. + * + * @return current context + */ + shared_ptr <context> getContext() const; + + /** Quick checks whether this implementation can read the Maildir + * format in the specified directory. + * + * @return true if the implementation supports the specified + * Maildir, or false otherwise + */ + virtual bool supports() const = 0; + +private: + + shared_ptr <context> m_context; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_FORMAT_MAILDIRFORMAT_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/maildir/maildirMessage.cpp b/vmime-master/src/vmime/net/maildir/maildirMessage.cpp new file mode 100644 index 0000000..ae99c59 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessage.cpp @@ -0,0 +1,410 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirMessage.hpp" +#include "vmime/net/maildir/maildirMessagePart.hpp" +#include "vmime/net/maildir/maildirMessageStructure.hpp" +#include "vmime/net/maildir/maildirFolder.hpp" +#include "vmime/net/maildir/maildirUtils.hpp" +#include "vmime/net/maildir/maildirStore.hpp" + +#include "vmime/message.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/stringUtils.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirMessage::maildirMessage(const shared_ptr <maildirFolder>& folder, const size_t num) + : m_folder(folder), + m_num(num), + m_size(-1), + m_flags(FLAG_UNDEFINED), + m_expunged(false), + m_structure(null) { + + folder->registerMessage(this); +} + + +maildirMessage::~maildirMessage() { + + try { + + shared_ptr <maildirFolder> folder = m_folder.lock(); + + if (folder) { + folder->unregisterMessage(this); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void maildirMessage::onFolderClosed() { + + m_folder.reset(); +} + + +size_t maildirMessage::getNumber() const { + + return m_num; +} + + +const message::uid maildirMessage::getUID() const { + + return m_uid; +} + + +size_t maildirMessage::getSize() const { + + if (m_size == static_cast <size_t>(-1)) { + throw exceptions::unfetched_object(); + } + + return m_size; +} + + +bool maildirMessage::isExpunged() const { + + return m_expunged; +} + + +shared_ptr <const messageStructure> maildirMessage::getStructure() const { + + if (!m_structure) { + throw exceptions::unfetched_object(); + } + + return m_structure; +} + + +shared_ptr <messageStructure> maildirMessage::getStructure() { + + if (!m_structure) { + throw exceptions::unfetched_object(); + } + + return m_structure; +} + + +shared_ptr <const header> maildirMessage::getHeader() const { + + if (!m_header) { + throw exceptions::unfetched_object(); + } + + return m_header; +} + + +int maildirMessage::getFlags() const { + + if (m_flags == FLAG_UNDEFINED) { + throw exceptions::unfetched_object(); + } + + return m_flags; +} + + +void maildirMessage::setFlags(const int flags, const int mode) { + + shared_ptr <maildirFolder> folder = m_folder.lock(); + + if (!folder) { + throw exceptions::folder_not_found(); + } + + folder->setMessageFlags(messageSet::byNumber(m_num), flags, mode); +} + + +void maildirMessage::extract( + utility::outputStream& os, + utility::progressListener* progress, + const size_t start, + const size_t length, + const bool peek +) const { + + extractImpl(os, progress, 0, m_size, start, length, peek); +} + + +void maildirMessage::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 maildirMessagePart> mp = dynamicCast <const maildirMessagePart>(p); + + extractImpl( + os, progress, mp->getBodyParsedOffset(), mp->getBodyParsedLength(), + start, length, peek + ); +} + + +void maildirMessage::extractImpl( + utility::outputStream& os, + utility::progressListener* progress, + const size_t start, + const size_t length, + const size_t partialStart, + const size_t partialLength, + const bool /* peek */ +) const { + + shared_ptr <const maildirFolder> folder = m_folder.lock(); + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + const utility::file::path path = folder->getMessageFSPath(m_num); + shared_ptr <utility::file> file = fsf->create(path); + + shared_ptr <utility::fileReader> reader = file->getFileReader(); + shared_ptr <utility::inputStream> is = reader->getInputStream(); + + is->skip(start + partialStart); + + byte_t buffer[8192]; + size_t remaining = + (partialLength == static_cast <size_t>(-1) + ? length + : std::min(partialLength, length) + ); + + const size_t total = remaining; + size_t current = 0; + + if (progress) { + progress->start(total); + } + + while (!is->eof() && remaining > 0) { + + const size_t read = is->read(buffer, std::min(remaining, sizeof(buffer))); + + remaining -= read; + current += read; + + os.write(buffer, read); + + if (progress) { + progress->progress(current, total); + } + } + + if (progress) { + progress->stop(total); + } + + // TODO: mark as read unless 'peek' is set +} + + +void maildirMessage::fetchPartHeader(const shared_ptr <messagePart>& p) { + + shared_ptr <maildirFolder> folder = m_folder.lock(); + + shared_ptr <maildirMessagePart> mp = dynamicCast <maildirMessagePart>(p); + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + const utility::file::path path = folder->getMessageFSPath(m_num); + shared_ptr <utility::file> file = fsf->create(path); + + shared_ptr <utility::fileReader> reader = file->getFileReader(); + shared_ptr <utility::inputStream> is = reader->getInputStream(); + + is->skip(mp->getHeaderParsedOffset()); + + byte_t buffer[1024]; + size_t remaining = mp->getHeaderParsedLength(); + + string contents; + contents.reserve(remaining); + + while (!is->eof() && remaining > 0) { + + const size_t read = is->read(buffer, std::min(remaining, sizeof(buffer))); + + remaining -= read; + + vmime::utility::stringUtils::appendBytesToString(contents, buffer, read); + } + + mp->getOrCreateHeader().parse(contents); +} + + +void maildirMessage::fetch(const shared_ptr <maildirFolder>& msgFolder, const fetchAttributes& options) { + + shared_ptr <maildirFolder> folder = m_folder.lock(); + + if (folder != msgFolder) { + throw exceptions::folder_not_found(); + } + + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + const utility::file::path path = folder->getMessageFSPath(m_num); + shared_ptr <utility::file> file = fsf->create(path); + + if (options.has(fetchAttributes::FLAGS)) { + m_flags = maildirUtils::extractFlags(path.getLastComponent()); + } + + if (options.has(fetchAttributes::SIZE)) { + m_size = file->getLength(); + } + + if (options.has(fetchAttributes::UID)) { + m_uid = maildirUtils::extractId(path.getLastComponent()).getBuffer(); + } + + if (options.has(fetchAttributes::ENVELOPE | fetchAttributes::CONTENT_INFO | + fetchAttributes::FULL_HEADER | fetchAttributes::STRUCTURE | + fetchAttributes::IMPORTANCE)) { + + string contents; + + shared_ptr <utility::fileReader> reader = file->getFileReader(); + shared_ptr <utility::inputStream> is = reader->getInputStream(); + + // Need whole message contents for structure + if (options.has(fetchAttributes::STRUCTURE)) { + + byte_t buffer[16384]; + + contents.reserve(file->getLength()); + + while (!is->eof()) { + const size_t read = is->read(buffer, sizeof(buffer)); + vmime::utility::stringUtils::appendBytesToString(contents, buffer, read); + } + + // Need only header + } else { + + byte_t buffer[1024]; + + contents.reserve(4096); + + while (!is->eof()) { + + const size_t read = is->read(buffer, sizeof(buffer)); + vmime::utility::stringUtils::appendBytesToString(contents, buffer, read); + + const size_t sep1 = contents.rfind("\r\n\r\n"); + const size_t sep2 = contents.rfind("\n\n"); + + if (sep1 != string::npos) { + contents.erase(contents.begin() + sep1 + 4, contents.end()); + break; + } else if (sep2 != string::npos) { + contents.erase(contents.begin() + sep2 + 2, contents.end()); + break; + } + } + } + + vmime::message msg; + msg.parse(contents); + + // Extract structure + if (options.has(fetchAttributes::STRUCTURE)) { + m_structure = make_shared <maildirMessageStructure>(shared_ptr <maildirMessagePart>(), msg); + } + + // Extract some header fields or whole header + if (options.has(fetchAttributes::ENVELOPE | + fetchAttributes::CONTENT_INFO | + fetchAttributes::FULL_HEADER | + fetchAttributes::IMPORTANCE)) { + + getOrCreateHeader()->copyFrom(*(msg.getHeader())); + } + } +} + + +shared_ptr <header> maildirMessage::getOrCreateHeader() { + + if (m_header) { + return m_header; + } else { + return (m_header = make_shared <header>()); + } +} + + +shared_ptr <vmime::message> maildirMessage::getParsedMessage() { + + std::ostringstream oss; + utility::outputStreamAdapter os(oss); + + extract(os); + + shared_ptr <vmime::message> msg = make_shared <vmime::message>(); + msg->parse(oss.str()); + + return msg; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirMessage.hpp b/vmime-master/src/vmime/net/maildir/maildirMessage.hpp new file mode 100644 index 0000000..8cd0aac --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessage.hpp @@ -0,0 +1,137 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/message.hpp" +#include "vmime/net/folder.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirFolder; + + +/** maildir message implementation. + */ +class VMIME_EXPORT maildirMessage : public message { + + friend class maildirFolder; + + maildirMessage(const maildirMessage&) : message() { } + +public: + + maildirMessage(const shared_ptr <maildirFolder>& folder, const size_t num); + + ~maildirMessage(); + + + size_t getNumber() const; + + const uid getUID() const; + + size_t getSize() const; + + bool isExpunged() const; + + shared_ptr <const messageStructure> getStructure() const; + shared_ptr <messageStructure> getStructure(); + + shared_ptr <const header> getHeader() const; + + int getFlags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract( + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void extractPart( + const shared_ptr <const messagePart>& p, + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void fetchPartHeader(const shared_ptr <messagePart>& p); + + shared_ptr <vmime::message> getParsedMessage(); + +private: + + void fetch(const shared_ptr <maildirFolder>& folder, const fetchAttributes& options); + + void onFolderClosed(); + + shared_ptr <header> getOrCreateHeader(); + + void extractImpl( + utility::outputStream& os, + utility::progressListener* progress, + const size_t start, + const size_t length, + const size_t partialStart, + const size_t partialLength, + const bool peek + ) const; + + + weak_ptr <maildirFolder> m_folder; + + size_t m_num; + size_t m_size; + int m_flags; + bool m_expunged; + uid m_uid; + + shared_ptr <header> m_header; + shared_ptr <messageStructure> m_structure; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirMessagePart.cpp b/vmime-master/src/vmime/net/maildir/maildirMessagePart.cpp new file mode 100644 index 0000000..7448d7e --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessagePart.cpp @@ -0,0 +1,178 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirMessagePart.hpp" +#include "vmime/net/maildir/maildirMessageStructure.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirMessagePart::maildirMessagePart( + const shared_ptr <maildirMessagePart>& parent, + const size_t number, + const bodyPart& part +) + : m_parent(parent), + m_header(null), + m_number(number) { + + m_headerParsedOffset = part.getHeader()->getParsedOffset(); + m_headerParsedLength = part.getHeader()->getParsedLength(); + + m_bodyParsedOffset = part.getBody()->getParsedOffset(); + m_bodyParsedLength = part.getBody()->getParsedLength(); + + m_size = part.getBody()->getContents()->getLength(); + + m_mediaType = part.getBody()->getContentType(); + + auto cdispField = part.getHeader()->ContentDisposition(); + if (cdispField) { + m_dispType = dynamic_cast <const contentDisposition&>(*cdispField->getValue()); + } +} + + +maildirMessagePart::~maildirMessagePart() { + +} + + +void maildirMessagePart::initStructure(const bodyPart& part) { + + if (part.getBody()->getPartList().size() == 0) { + + m_structure = null; + + } else { + + m_structure = make_shared <maildirMessageStructure>( + dynamicCast <maildirMessagePart>(shared_from_this()), part.getBody()->getPartList() + ); + } +} + + +shared_ptr <const messageStructure> maildirMessagePart::getStructure() const { + + if (m_structure) { + return m_structure; + } else { + return maildirMessageStructure::emptyStructure(); + } +} + + +shared_ptr <messageStructure> maildirMessagePart::getStructure() { + + if (m_structure) { + return m_structure; + } else { + return maildirMessageStructure::emptyStructure(); + } +} + + +const mediaType& maildirMessagePart::getType() const { + + return m_mediaType; +} + + +const contentDisposition &maildirMessagePart::getDisposition() const { + + return m_dispType; +} + + +size_t maildirMessagePart::getSize() const { + + return m_size; +} + + +size_t maildirMessagePart::getNumber() const { + + return m_number; +} + + +shared_ptr <const header> maildirMessagePart::getHeader() const { + + if (!m_header) { + throw exceptions::unfetched_object(); + } else { + return m_header; + } +} + + +header& maildirMessagePart::getOrCreateHeader() { + + if (m_header) { + return *m_header; + } else { + return *(m_header = make_shared <header>()); + } +} + + +size_t maildirMessagePart::getHeaderParsedOffset() const { + + return m_headerParsedOffset; +} + + +size_t maildirMessagePart::getHeaderParsedLength() const { + + return m_headerParsedLength; +} + + +size_t maildirMessagePart::getBodyParsedOffset() const { + + return m_bodyParsedOffset; +} + + +size_t maildirMessagePart::getBodyParsedLength() const { + + return m_bodyParsedLength; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR diff --git a/vmime-master/src/vmime/net/maildir/maildirMessagePart.hpp b/vmime-master/src/vmime/net/maildir/maildirMessagePart.hpp new file mode 100644 index 0000000..5ecc739 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessagePart.hpp @@ -0,0 +1,106 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRMESSAGEPART_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRMESSAGEPART_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/message.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirMessageStructure; + + +class maildirMessagePart : public messagePart +{ +public: + + maildirMessagePart( + const shared_ptr <maildirMessagePart>& parent, + const size_t number, + const bodyPart& part + ); + + ~maildirMessagePart(); + + + shared_ptr <const messageStructure> getStructure() const; + shared_ptr <messageStructure> getStructure(); + + weak_ptr <const maildirMessagePart> getParent() const { return (m_parent); } + + const mediaType& getType() const; + const contentDisposition &getDisposition() const; + size_t getSize() const; + size_t getNumber() const; + + shared_ptr <const header> getHeader() const; + + header& getOrCreateHeader(); + + size_t getHeaderParsedOffset() const; + size_t getHeaderParsedLength() const; + + size_t getBodyParsedOffset() const; + size_t getBodyParsedLength() const; + + void initStructure(const bodyPart& part); + +private: + + shared_ptr <maildirMessageStructure> m_structure; + weak_ptr <maildirMessagePart> m_parent; + shared_ptr <header> m_header; + + size_t m_number; + size_t m_size; + mediaType m_mediaType; + contentDisposition m_dispType; + + size_t m_headerParsedOffset; + size_t m_headerParsedLength; + + size_t m_bodyParsedOffset; + size_t m_bodyParsedLength; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRMESSAGEPART_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirMessageStructure.cpp b/vmime-master/src/vmime/net/maildir/maildirMessageStructure.cpp new file mode 100644 index 0000000..1e2f2cc --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessageStructure.cpp @@ -0,0 +1,104 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirMessageStructure.hpp" +#include "vmime/net/maildir/maildirMessagePart.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +shared_ptr <maildirMessageStructure> maildirMessageStructure::m_emptyStructure = make_shared <maildirMessageStructure>(); + + +maildirMessageStructure::maildirMessageStructure() +{ +} + + +maildirMessageStructure::maildirMessageStructure( + const shared_ptr <maildirMessagePart>& parent, + const bodyPart& part +) { + + shared_ptr <maildirMessagePart> mpart = make_shared <maildirMessagePart>(parent, 0, part); + mpart->initStructure(part); + + m_parts.push_back(mpart); +} + + +maildirMessageStructure::maildirMessageStructure( + const shared_ptr <maildirMessagePart>& parent, + const std::vector <shared_ptr <const vmime::bodyPart> >& list +) { + + for (size_t i = 0 ; i < list.size() ; ++i) { + + shared_ptr <maildirMessagePart> mpart = make_shared <maildirMessagePart>(parent, i, *list[i]); + mpart->initStructure(*list[i]); + + m_parts.push_back(mpart); + } +} + + +shared_ptr <const messagePart> maildirMessageStructure::getPartAt(const size_t x) const { + + return m_parts[x]; +} + + +shared_ptr <messagePart> maildirMessageStructure::getPartAt(const size_t x) { + + return m_parts[x]; +} + + +size_t maildirMessageStructure::getPartCount() const { + + return m_parts.size(); +} + + +// static +shared_ptr <maildirMessageStructure> maildirMessageStructure::emptyStructure() { + + return m_emptyStructure; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR diff --git a/vmime-master/src/vmime/net/maildir/maildirMessageStructure.hpp b/vmime-master/src/vmime/net/maildir/maildirMessageStructure.hpp new file mode 100644 index 0000000..1a20ea4 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirMessageStructure.hpp @@ -0,0 +1,82 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRMESSAGESTRUCTURE_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRMESSAGESTRUCTURE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/message.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirMessagePart; + + +class maildirMessageStructure : public messageStructure { + +public: + + maildirMessageStructure(); + maildirMessageStructure( + const shared_ptr <maildirMessagePart>& parent, + const bodyPart& part + ); + maildirMessageStructure( + const shared_ptr <maildirMessagePart>& parent, + const std::vector <shared_ptr <const vmime::bodyPart> >& list + ); + + + shared_ptr <const messagePart> getPartAt(const size_t x) const; + shared_ptr <messagePart> getPartAt(const size_t x); + + size_t getPartCount() const; + + static shared_ptr <maildirMessageStructure> emptyStructure(); + +private: + + static shared_ptr <maildirMessageStructure> m_emptyStructure; + + std::vector <shared_ptr <maildirMessagePart> > m_parts; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRMESSAGESTRUCTURE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirServiceInfos.cpp b/vmime-master/src/vmime/net/maildir/maildirServiceInfos.cpp new file mode 100644 index 0000000..f9d92d3 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirServiceInfos.cpp @@ -0,0 +1,76 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirServiceInfos::maildirServiceInfos() { + +} + + +const string maildirServiceInfos::getPropertyPrefix() const { + + return "store.maildir."; +} + + +const maildirServiceInfos::props& maildirServiceInfos::getProperties() const { + + static props maildirProps = { + property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED) + }; + + return maildirProps; +} + + +const std::vector <serviceInfos::property> maildirServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + list.push_back(p.PROPERTY_SERVER_ROOTPATH); + + return list; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirServiceInfos.hpp b/vmime-master/src/vmime/net/maildir/maildirServiceInfos.hpp new file mode 100644 index 0000000..827a7d7 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirServiceInfos.hpp @@ -0,0 +1,69 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +/** Information about maildir service. + */ +class VMIME_EXPORT maildirServiceInfos : public serviceInfos { + +public: + + maildirServiceInfos(); + + struct props { + serviceInfos::property PROPERTY_SERVER_ROOTPATH; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/maildir/maildirStore.cpp b/vmime-master/src/vmime/net/maildir/maildirStore.cpp new file mode 100644 index 0000000..a994f45 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirStore.cpp @@ -0,0 +1,294 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirStore.hpp" + +#include "vmime/net/maildir/maildirFolder.hpp" +#include "vmime/net/maildir/maildirFormat.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const maildirServiceInfos&>(getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (getInfos().hasProperty(getSession(), \ + dynamic_cast <const maildirServiceInfos&>(getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirStore::maildirStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : store(sess, getInfosInstance(), auth), + m_connected(false) { + +} + + +maildirStore::~maildirStore() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string maildirStore::getProtocolName() const { + + return "maildir"; +} + + +shared_ptr <folder> maildirStore::getRootFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <maildirFolder>( + new maildirFolder( + folder::path(), + dynamicCast <maildirStore>(shared_from_this()) + ) + ); +} + + +shared_ptr <folder> maildirStore::getDefaultFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <maildirFolder>( + new maildirFolder( + folder::path::component("inbox"), + dynamicCast <maildirStore>(shared_from_this()) + ) + ); +} + + +shared_ptr <folder> maildirStore::getFolder(const folder::path& path) { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <maildirFolder>( + new maildirFolder( + path, + dynamicCast <maildirStore>(shared_from_this()) + ) + ); +} + + +bool maildirStore::isValidFolderName(const folder::path::component& name) const { + + if (!platform::getHandler()->getFileSystemFactory()->isValidPathComponent(name)) { + return false; + } + + const string& buf = name.getBuffer(); + + // Name cannot start/end with spaces + if (utility::stringUtils::trim(buf) != buf) { + return false; + } + + // Name cannot start with '.' + const size_t length = buf.length(); + size_t pos = 0; + + while ((pos < length) && (buf[pos] == '.')) { + ++pos; + } + + return (pos == 0); +} + + +void maildirStore::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + // Get root directory + shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory(); + + m_fsPath = fsf->stringToPath(GET_PROPERTY(string, PROPERTY_SERVER_ROOTPATH)); + + shared_ptr <utility::file> rootDir = fsf->create(m_fsPath); + + // Try to create the root directory if it does not exist + if (!(rootDir->exists() && rootDir->isDirectory())) { + + try { + rootDir->createDirectory(); + } catch (exceptions::filesystem_exception& e) { + throw exceptions::connection_error("Cannot create root directory.", e); + } + } + + m_format = maildirFormat::detect(dynamicCast <maildirStore>(shared_from_this())); + + m_connected = true; +} + + +bool maildirStore::isConnected() const { + + return m_connected; +} + + +bool maildirStore::isSecuredConnection() const { + + return false; +} + + +shared_ptr <connectionInfos> maildirStore::getConnectionInfos() const { + + return make_shared <defaultConnectionInfos>("localhost", static_cast <port_t>(0)); +} + + +void maildirStore::disconnect() { + + for (std::list <maildirFolder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) { + + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + m_connected = false; +} + + +void maildirStore::noop() { + + // Nothing to do. +} + + +shared_ptr <maildirFormat> maildirStore::getFormat() { + + return m_format; +} + + +shared_ptr <const maildirFormat> maildirStore::getFormat() const { + + return m_format; +} + + +void maildirStore::registerFolder(maildirFolder* folder) { + + m_folders.push_back(folder); +} + + +void maildirStore::unregisterFolder(maildirFolder* folder) { + + std::list <maildirFolder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + + if (it != m_folders.end()) { + m_folders.erase(it); + } +} + + +const utility::path& maildirStore::getFileSystemPath() const { + + return m_fsPath; +} + + +int maildirStore::getCapabilities() const { + + return CAPABILITY_CREATE_FOLDER | + CAPABILITY_RENAME_FOLDER | + CAPABILITY_ADD_MESSAGE | + CAPABILITY_COPY_MESSAGE | + CAPABILITY_DELETE_MESSAGE | + CAPABILITY_PARTIAL_FETCH | + CAPABILITY_MESSAGE_FLAGS | + CAPABILITY_EXTRACT_PART; +} + + + +// Service infos + +maildirServiceInfos maildirStore::sm_infos; + + +const serviceInfos& maildirStore::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& maildirStore::getInfos() const { + + return sm_infos; +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirStore.hpp b/vmime-master/src/vmime/net/maildir/maildirStore.hpp new file mode 100644 index 0000000..13255c0 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirStore.hpp @@ -0,0 +1,123 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/net/store.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/folder.hpp" + +#include "vmime/net/maildir/maildirFormat.hpp" +#include "vmime/net/maildir/maildirServiceInfos.hpp" + +#include "vmime/utility/file.hpp" + +#include <ostream> + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirFolder; + + +/** maildir store service. + */ +class VMIME_EXPORT maildirStore : public store { + + friend class maildirFolder; + +public: + + maildirStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ); + + ~maildirStore(); + + const string getProtocolName() const; + + shared_ptr <folder> getDefaultFolder(); + shared_ptr <folder> getRootFolder(); + shared_ptr <folder> getFolder(const folder::path& path); + + bool isValidFolderName(const folder::path::component& name) const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + const utility::path& getFileSystemPath() const; + + int getCapabilities() const; + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + + shared_ptr <maildirFormat> getFormat(); + shared_ptr <const maildirFormat> getFormat() const; + +private: + + void registerFolder(maildirFolder* folder); + void unregisterFolder(maildirFolder* folder); + + + std::list <maildirFolder*> m_folders; + + shared_ptr <maildirFormat> m_format; + + bool m_connected; + + utility::path m_fsPath; + + + // Service infos + static maildirServiceInfos sm_infos; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/maildir/maildirUtils.cpp b/vmime-master/src/vmime/net/maildir/maildirUtils.cpp new file mode 100644 index 0000000..9942e56 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirUtils.cpp @@ -0,0 +1,288 @@ +// +// 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_MAILDIR + + +#include "vmime/net/maildir/maildirUtils.hpp" +#include "vmime/net/maildir/maildirStore.hpp" + +#include "vmime/utility/random.hpp" +#include "vmime/platform.hpp" + +#include "vmime/exception.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +bool maildirUtils::isMessageFile(const utility::file& file) { + + // Ignore files which name begins with '.' + if (file.isFile() && + file.getFullPath().getLastComponent().getBuffer().length() >= 1 && + file.getFullPath().getLastComponent().getBuffer()[0] != '.') { + + return true; + } + + return false; +} + + +// NOTE ABOUT ID/FLAGS SEPARATOR +// ----------------------------- +// In the maildir specification, the character ':' is used to separate +// the unique identifier and the message flags. +// +// On Windows (and particularly FAT file systems), ':' is not allowed +// in a filename, so we use a dash ('-') instead. This is the solution +// used by Mutt/Win32, so we also use it here. +// +// To be compatible between implementations, we check for both +// characters when reading file names. + + +const utility::file::path::component maildirUtils::extractId( + const utility::file::path::component& filename +) { + + size_t sep = filename.getBuffer().rfind(':'); // try colon + + if (sep == string::npos) { + sep = filename.getBuffer().rfind('-'); // try dash (Windows) + if (sep == string::npos) return (filename); + } + + return utility::path::component( + string(filename.getBuffer().begin(), filename.getBuffer().begin() + sep) + ); +} + + +int maildirUtils::extractFlags(const utility::file::path::component& comp) { + + size_t sep = comp.getBuffer().rfind(':'); // try colon + + if (sep == string::npos) { + sep = comp.getBuffer().rfind('-'); // try dash (Windows) + if (sep == string::npos) return 0; + } + + const string flagsString(comp.getBuffer().begin() + sep + 1, comp.getBuffer().end()); + const size_t count = flagsString.length(); + + int flags = 0; + + for (size_t i = 0 ; i < count ; ++i) { + + switch (flagsString[i]) { + case 'R': case 'r': flags |= message::FLAG_REPLIED; break; + case 'S': case 's': flags |= message::FLAG_SEEN; break; + case 'T': case 't': flags |= message::FLAG_DELETED; break; + case 'F': case 'f': flags |= message::FLAG_MARKED; break; + case 'P': case 'p': flags |= message::FLAG_PASSED; break; + case 'D': case 'd': flags |= message::FLAG_DRAFT; break; + } + } + + return flags; +} + + +const utility::file::path::component maildirUtils::buildFlags(const int flags) { + + string str; + str.reserve(8); + + str += "2,"; + + if (flags & message::FLAG_MARKED) str += "F"; + if (flags & message::FLAG_PASSED) str += "P"; + if (flags & message::FLAG_REPLIED) str += "R"; + if (flags & message::FLAG_SEEN) str += "S"; + if (flags & message::FLAG_DELETED) str += "T"; + if (flags & message::FLAG_DRAFT) str += "D"; + + return utility::file::path::component(str); +} + + +const utility::file::path::component maildirUtils::buildFilename( + const utility::file::path::component& id, + const int flags +) { + + if (flags == message::FLAG_RECENT) { + return id; + } else { + return buildFilename(id, buildFlags(flags)); + } +} + + +const utility::file::path::component maildirUtils::buildFilename( + const utility::file::path::component& id, + const utility::file::path::component& flags +) { + +#if VMIME_PLATFORM_IS_WINDOWS + static const char DELIMITER[] = "-"; +#else + static const char DELIMITER[] = ":"; +#endif + + return utility::path::component(id.getBuffer() + DELIMITER + flags.getBuffer()); +} + + +const utility::file::path::component maildirUtils::generateId() { + + std::ostringstream oss; + oss.imbue(std::locale::classic()); + + oss << utility::random::getTime(); + oss << "."; + oss << utility::random::getProcess(); + oss << "."; + oss << utility::random::getString(6); + oss << "."; + oss << platform::getHandler()->getHostName(); + + return utility::file::path::component(oss.str()); +} + + +void maildirUtils::recursiveFSDelete(const shared_ptr <utility::file>& dir) { + + shared_ptr <utility::fileIterator> files = dir->getFiles(); + + // First, delete files and subdirectories in this directory + while (files->hasMoreElements()) { + + shared_ptr <utility::file> file = files->nextElement(); + + if (file->isDirectory()) { + + maildirUtils::recursiveFSDelete(file); + + } else { + + try { + file->remove(); + } catch (exceptions::filesystem_exception&) { + // Ignore + } + } + } + + // Then, delete this (empty) directory + try { + dir->remove(); + } catch (exceptions::filesystem_exception&) { + // Ignore + } +} + + + +class maildirMessageSetEnumerator : public messageSetEnumerator { + +public: + + maildirMessageSetEnumerator(const size_t msgCount) + : m_msgCount(msgCount) { + + } + + void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range) { + + size_t last = range.getLast(); + if (last == size_t(-1)) last = m_msgCount; + + for (size_t i = range.getFirst() ; i <= last ; ++i) { + list.push_back(i); + } + } + + void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */) { + + // Not supported + } + +public: + + std::vector <size_t> list; + +private: + + size_t m_msgCount; +}; + + +// static +const std::vector <size_t> maildirUtils::messageSetToNumberList( + const messageSet& msgs, + const size_t msgCount +) { + + maildirMessageSetEnumerator en(msgCount); + msgs.enumerate(en); + + return en.list; +} + + + +// +// messageIdComparator +// + +maildirUtils::messageIdComparator::messageIdComparator( + const utility::file::path::component& comp +) + : m_comp(maildirUtils::extractId(comp)) { + +} + + +bool maildirUtils::messageIdComparator::operator()( + const utility::file::path::component& other +) const { + + return m_comp == maildirUtils::extractId(other); +} + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + diff --git a/vmime-master/src/vmime/net/maildir/maildirUtils.hpp b/vmime-master/src/vmime/net/maildir/maildirUtils.hpp new file mode 100644 index 0000000..94ab998 --- /dev/null +++ b/vmime-master/src/vmime/net/maildir/maildirUtils.hpp @@ -0,0 +1,160 @@ +// +// 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. +// + +#ifndef VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + + +#include "vmime/utility/file.hpp" +#include "vmime/utility/path.hpp" + +#include "vmime/net/messageSet.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +class maildirStore; + + +/** Miscellaneous helpers functions for maildir messaging system. + */ +class VMIME_EXPORT maildirUtils { + +public: + + /** Comparator for message filenames, based only on the + * unique identifier part of the filename. + */ + class messageIdComparator { + + public: + + messageIdComparator(const utility::file::path::component& comp); + + bool operator()(const utility::file::path::component& other) const; + + private: + + const utility::file::path::component m_comp; + }; + + /** Test whether the specified file-system object is a message. + * + * @param file reference to a file-system object + * @return true if the specified object is a message file, + * false otherwise + */ + static bool isMessageFile(const utility::file& file); + + /** Extract the unique identifier part of the message filename. + * Eg: for the filename "1071577232.28549.m03s:2,RS", it will + * return "1071577232.28549.m03s". + * + * @param filename filename part + * @return part of the filename that corresponds to the unique + * identifier of the message + */ + static const utility::file::path::component extractId(const utility::file::path::component& filename); + + /** Extract message flags from the specified message filename. + * Eg: for the filename "1071577232.28549.m03s:2,RS", it will + * return (message::FLAG_SEEN | message::FLAG_REPLIED). + * + * @param comp filename part + * @return message flags extracted from the specified filename + */ + static int extractFlags(const utility::file::path::component& comp); + + /** Return a string representing the specified message flags. + * Eg: for (message::FLAG_SEEN | message::FLAG_REPLIED), it will + * return "RS". + * + * @param flags set of flags + * @return message flags in a string representation + */ + static const utility::file::path::component buildFlags(const int flags); + + /** Build a filename with the specified id and flags. + * + * @param id id part of the filename + * @param flags flags part of the filename + * @return message filename + */ + static const utility::file::path::component buildFilename( + const utility::file::path::component& id, + const utility::file::path::component& flags + ); + + /** Build a filename with the specified id and flags. + * + * @param id id part of the filename + * @param flags set of flags + * @return message filename + */ + static const utility::file::path::component buildFilename( + const utility::file::path::component& id, + const int flags + ); + + /** Generate a new unique message identifier. + * + * @return unique message id + */ + static const utility::file::path::component generateId(); + + /** Recursively delete a directory on the file system. + * + * @param dir directory to delete + */ + static void recursiveFSDelete(const shared_ptr <utility::file>& dir); + + /** Returns a list of message numbers given a message set. + * + * @param msgs message set + * @param msgCount number of messages in folder + * @return list of message numbers + */ + static const std::vector <size_t> messageSetToNumberList( + const messageSet& msgs, + const size_t msgCount + ); +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR + +#endif // VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/message.cpp b/vmime-master/src/vmime/net/message.cpp new file mode 100644 index 0000000..2d6f995 --- /dev/null +++ b/vmime-master/src/vmime/net/message.cpp @@ -0,0 +1,155 @@ +// +// 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 + + +#include "vmime/net/message.hpp" + +#include <sstream> + + +namespace vmime { +namespace net { + + +string messagePart::getName() const +{ + return {}; +} + +shared_ptr <const messagePart> messagePart::getPartAt(const size_t pos) const { + + return getStructure()->getPartAt(pos); +} + + +shared_ptr <messagePart> messagePart::getPartAt(const size_t pos) { + + return getStructure()->getPartAt(pos); +} + + +size_t messagePart::getPartCount() const { + + return getStructure()->getPartCount(); +} + + + +// message::uid + + +message::uid::uid() { + +} + + +message::uid::uid(const string& uid) + : m_str(uid) { + +} + + +message::uid::uid(const unsigned long uid) { + + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << uid; + + m_str = oss.str(); +} + + +message::uid::uid(const char* uid) + : m_str(uid) { + +} + + +message::uid::uid(const uid& other) { + + m_str = other.m_str; +} + + +message::uid& message::uid::operator=(const uid& other) { + + m_str = other.m_str; + return *this; +} + + +message::uid& message::uid::operator=(const string& uid) { + + m_str = uid; + return *this; +} + + +message::uid& message::uid::operator=(const unsigned long uid) { + + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << uid; + + m_str = oss.str(); + + return *this; +} + + +message::uid::operator string() const { + + return m_str; +} + + +bool message::uid::empty() const { + + return m_str.empty(); +} + + +bool message::uid::operator==(const uid& other) const { + + return m_str == other.m_str; +} + + +std::ostream& operator<<(std::ostream& os, const message::uid& uid) { + + os << static_cast <string>(uid); + return os; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/message.hpp b/vmime-master/src/vmime/net/message.hpp new file mode 100644 index 0000000..8679879 --- /dev/null +++ b/vmime-master/src/vmime/net/message.hpp @@ -0,0 +1,369 @@ +// +// 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. +// + +#ifndef VMIME_NET_MESSAGE_HPP_INCLUDED +#define VMIME_NET_MESSAGE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/header.hpp" +#include "vmime/mediaType.hpp" +#include "vmime/contentDisposition.hpp" + +#include "vmime/utility/progressListener.hpp" +#include "vmime/utility/stream.hpp" + +#include "vmime/message.hpp" + + +namespace vmime { +namespace net { + + +class messageStructure; + + +/** A MIME part in a message. + */ +class VMIME_EXPORT messagePart : public object, public enable_shared_from_this <messagePart> { + +protected: + + messagePart() { } + messagePart(const messagePart&); + + virtual ~messagePart() { } + +public: + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual shared_ptr <const messageStructure> getStructure() const = 0; + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual shared_ptr <messageStructure> getStructure() = 0; + + /** Return the header section for this part (you must fetch header + * before using this function: see message::fetchPartHeader). + * + * @return header section + */ + virtual shared_ptr <const header> getHeader() const = 0; + + /** Return the media-type of the content in this part. + * + * @return content media type + */ + virtual const mediaType& getType() const = 0; + + /** Return the disposition type of the content in this part. + * + * @return content disposition type + */ + virtual const contentDisposition &getDisposition() const = 0; + + /** Return the size of this part. + * + * @return size of the part (in bytes) + */ + virtual size_t getSize() const = 0; + + /** Return the part sequence number (index). + * The first part is at index zero. + * + * @return part number + */ + virtual size_t getNumber() const = 0; + + /** Return the name of this part. In particular, this corresponds to + * the attachment file name for attachment parts. + * + * The part name may be empty if the part does not advertise it or + * if the underlying protocol does not support it. + * + * @return part name + */ + virtual string getName() const; + + /** Return the sub-part at the specified position (zero is the + * first part). + * + * @param pos index of the sub-part + * @return sub-part at position 'pos' + */ + shared_ptr <const messagePart> getPartAt(const size_t pos) const; + + /** Return the sub-part at the specified position (zero is the + * first part). + * + * @param pos index of the sub-part + * @return sub-part at position 'pos' + */ + shared_ptr <messagePart> getPartAt(const size_t pos); + + /** Return the number of sub-parts in this part. + * + * @return number of sub-parts + */ + size_t getPartCount() const; +}; + + +/** Structure of a MIME part/message. + */ +class VMIME_EXPORT messageStructure : public object, public enable_shared_from_this <messageStructure> { + +protected: + + messageStructure() { } + messageStructure(const messageStructure&); + +public: + + virtual ~messageStructure() { } + + /** Return the part at the specified position (first + * part is at position 0). + * + * @param pos position + * @return part at position 'pos' + */ + virtual shared_ptr <const messagePart> getPartAt(const size_t pos) const = 0; + + /** Return the part at the specified position (first + * part is at position 0). + * + * @param pos position + * @return part at position 'pos' + */ + virtual shared_ptr <messagePart> getPartAt(const size_t pos) = 0; + + /** Return the number of parts in this part. + * + * @return number of parts + */ + virtual size_t getPartCount() const = 0; +}; + + +/** Abstract representation of a message in a store/transport service. + */ +class VMIME_EXPORT message : public object, public enable_shared_from_this <message> { + +protected: + + message() { } + message(const message&); + + enum PrivateConstants { + FLAG_UNDEFINED = 9999 /**< Used internally to indicate flags have not + been initialized yet. */ + }; + +public: + + /** The type for an unique message identifier. + */ + class VMIME_EXPORT uid { + + public: + + uid(); + uid(const string& uid); + uid(const unsigned long uid); + uid(const char* uid); + uid(const uid& other); + + uid& operator=(const uid& other); + uid& operator=(const string& uid); + uid& operator=(const unsigned long uid); + + operator string() const; + + bool empty() const; + + bool operator==(const uid& other) const; + + private: + + string m_str; + }; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual shared_ptr <const messageStructure> getStructure() const = 0; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual shared_ptr <messageStructure> getStructure() = 0; + + /** Return a reference to the header fields of the message (must fetch before). + * + * @return header section of the message + */ + virtual shared_ptr <const header> getHeader() const = 0; + + /** Return the sequence number of this message. This number is + * used to reference the message in the folder. + * + * @return sequence number of the message + */ + virtual size_t getNumber() const = 0; + + /** Return the unique identifier (UID) of this message in its + * folder (must fetch before). + * + * @return UID of the message + */ + virtual const uid getUID() const = 0; + + /** Return the size of the message (must fetch before). + * + * @return size of the message (in bytes) + */ + virtual size_t getSize() const = 0; + + /** Check whether this message has been expunged (ie: definitively + * deleted) and does not exist in the folder anymore. + * + * @return true if the message is expunged, false otherwise + */ + virtual bool isExpunged() const = 0; + + /** Possible flags for a message. + */ + enum Flags { + FLAG_SEEN = (1 << 0), /**< Message has been seen. */ + FLAG_RECENT = (1 << 1), /**< Message has been recently received. */ + FLAG_DELETED = (1 << 2), /**< Message is marked for deletion. */ + FLAG_REPLIED = (1 << 3), /**< User replied to this message. */ + FLAG_MARKED = (1 << 4), /**< Used-defined flag. */ + FLAG_PASSED = (1 << 5), /**< Message has been resent/forwarded/bounced. */ + FLAG_DRAFT = (1 << 6) /**< Message is marked as a 'draft'. */ + }; + + /** Methods for setting the flags. + */ + enum FlagsModes { + FLAG_MODE_SET, /**< Set (replace) the flags. */ + FLAG_MODE_ADD, /**< Add the flags. */ + FLAG_MODE_REMOVE /**< Remove the flags. */ + }; + + /** Return the flags of this message. + * + * @return flags of the message + */ + virtual int getFlags() const = 0; + + /** Set the flags of this message. + * + * @param flags set of flags (see Flags) + * @param mode indicate how to treat old and new flags (see FlagsModes) + */ + virtual void setFlags(const int flags, const int mode = FLAG_MODE_SET) = 0; + + /** Extract the whole message data (header + contents). + * + * \warning Partial fetch might not be supported by the underlying protocol. + * + * @param os output stream in which to write message data + * @param progress progress listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + * @param peek if true, try not to mark the message as read. This may not + * be supported by the protocol (IMAP supports this), but it will NOT throw + * an exception if not supported. + */ + virtual void extract( + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const = 0; + + /** Extract the specified MIME part of the message (header + contents). + * + * \warning Partial fetch might not be supported by the underlying protocol. + * + * @param p part to extract + * @param os output stream in which to write part data + * @param progress progress listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + * @param peek if true, try not to mark the message as read. This may not + * be supported by the protocol (IMAP supports this), but it will NOT throw + * an exception if not supported. + */ + virtual void extractPart( + const shared_ptr <const messagePart>& p, + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const = 0; + + /** Fetch the MIME header for the specified part. + * + * @param p the part for which to fetch the header + */ + virtual void fetchPartHeader(const shared_ptr <messagePart>& p) = 0; + + /** Get the RFC-822 message for this abstract message. + * Warning: This may require getting some data (ie: structure and headers) from + * the server, which is done automatically. Actual message contents (ie: body) + * will not be fetched if possible (IMAP allows it, whereas POP3 will require + * to fetch the whole message). + * + * @return a RFC-822-parsed message + */ + virtual shared_ptr <vmime::message> getParsedMessage() = 0; +}; + + +VMIME_EXPORT std::ostream& operator<<(std::ostream& os, const message::uid& uid); + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_MESSAGE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/messageSet.cpp b/vmime-master/src/vmime/net/messageSet.cpp new file mode 100644 index 0000000..fe2d645 --- /dev/null +++ b/vmime-master/src/vmime/net/messageSet.cpp @@ -0,0 +1,430 @@ +// +// 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 + + +#include "vmime/net/messageSet.hpp" + +#include <iterator> +#include <algorithm> +#include <typeinfo> + + +namespace vmime { +namespace net { + +// messageSetEnumerator + +messageSetEnumerator::~messageSetEnumerator() +{ +} + +// messageRange + +messageRange::messageRange() { + +} + + +messageRange::~messageRange() { + +} + + +// numberMessageRange + +numberMessageRange::numberMessageRange(const size_t number) + : m_first(number), + m_last(number) { + + if (number < 1) { + throw std::invalid_argument("number"); + } +} + + +numberMessageRange::numberMessageRange(const size_t first, const size_t last) + : m_first(first), + m_last(last) { + + if (first < 1 || first == static_cast <size_t>(-1)) { + throw std::invalid_argument("first"); + } else if (last != static_cast <size_t>(-1) && last < first) { + throw std::invalid_argument("last"); + } +} + + +numberMessageRange::numberMessageRange(const numberMessageRange& other) + : messageRange(), + m_first(other.m_first), + m_last(other.m_last) { + +} + + +size_t numberMessageRange::getFirst() const { + + return m_first; +} + + +size_t numberMessageRange::getLast() const { + + return m_last; +} + + +void numberMessageRange::enumerate(messageSetEnumerator& en) const { + + en.enumerateNumberMessageRange(*this); +} + + +messageRange* numberMessageRange::clone() const { + + return new numberMessageRange(*this); +} + + +// UIDMessageRange + +UIDMessageRange::UIDMessageRange(const message::uid& uid) + : m_first(uid), + m_last(uid) { + +} + + +UIDMessageRange::UIDMessageRange(const message::uid& first, const message::uid& last) + : m_first(first), + m_last(last) { + +} + + +UIDMessageRange::UIDMessageRange(const UIDMessageRange& other) + : messageRange(), + m_first(other.m_first), + m_last(other.m_last) { + +} + + +const message::uid UIDMessageRange::getFirst() const { + + return m_first; +} + + +const message::uid UIDMessageRange::getLast() const { + + return m_last; +} + + +void UIDMessageRange::enumerate(messageSetEnumerator& en) const { + + en.enumerateUIDMessageRange(*this); +} + + +messageRange* UIDMessageRange::clone() const { + + return new UIDMessageRange(*this); +} + + +// messageSet + + +messageSet::messageSet() { + +} + + +messageSet::messageSet(const messageSet& other) + : object() { + + m_ranges.resize(other.m_ranges.size()); + + for (size_t i = 0, n = other.m_ranges.size() ; i < n ; ++i) { + m_ranges[i] = other.m_ranges[i]->clone(); + } +} + + +messageSet::~messageSet() { + + for (size_t i = 0, n = m_ranges.size() ; i < n ; ++i) { + delete m_ranges[i]; + } +} + + +// static +messageSet messageSet::empty() { + + return messageSet(); +} + + +// static +messageSet messageSet::byNumber(const size_t number) { + + messageSet set; + set.m_ranges.push_back(new numberMessageRange(number)); + + return set; +} + + +// static +messageSet messageSet::byNumber(const size_t first, const size_t last) { + + messageSet set; + set.m_ranges.push_back(new numberMessageRange(first, last)); + + return set; +} + + +// static +messageSet messageSet::byNumber(const std::vector <size_t>& numbers) { + + // Sort a copy of the list + std::vector <size_t> sortedNumbers; + + sortedNumbers.resize(numbers.size()); + + std::copy(numbers.begin(), numbers.end(), sortedNumbers.begin()); + std::sort(sortedNumbers.begin(), sortedNumbers.end()); + + // Build the set by detecting ranges of continuous numbers + size_t previous = static_cast <size_t>(-1), rangeStart = static_cast <size_t>(-1); + messageSet set; + + for (std::vector <size_t>::const_iterator it = sortedNumbers.begin() ; + it != sortedNumbers.end() ; ++it) { + + const size_t current = *it; + + if (current == previous) { + continue; // skip duplicates + } + + if (current == static_cast <size_t>(-1)) { + throw std::invalid_argument("numbers"); + } + + if (previous == static_cast <size_t>(-1)) { + + previous = current; + rangeStart = current; + + } else { + + if (current == previous + 1) { + + previous = current; + + } else { + + set.m_ranges.push_back(new numberMessageRange(rangeStart, previous)); + + previous = current; + rangeStart = current; + } + } + } + + set.m_ranges.push_back(new numberMessageRange(rangeStart, previous)); + + return set; +} + + +// static +messageSet messageSet::byUID(const message::uid& uid) { + + messageSet set; + set.m_ranges.push_back(new UIDMessageRange(uid)); + + return set; +} + + +messageSet messageSet::byUID(const message::uid& first, const message::uid& last) { + + messageSet set; + set.m_ranges.push_back(new UIDMessageRange(first, last)); + + return set; +} + + +messageSet messageSet::byUID(const std::vector <message::uid>& uids) { + + std::vector <vmime_uint32> numericUIDs; + + for (size_t i = 0, n = uids.size() ; i < n ; ++i) { + + const string uid = uids[i]; + int numericUID = 0; + + const char* p = uid.c_str(); + + for ( ; *p >= '0' && *p <= '9' ; ++p) { + numericUID = (numericUID * 10) + (*p - '0'); + } + + if (*p != '\0') { + + messageSet set; + + // Non-numeric UID, fall back to plain UID list (single-UID ranges) + for (size_t i = 0, n = uids.size() ; i < n ; ++i) { + set.m_ranges.push_back(new UIDMessageRange(uids[i])); + } + + return set; + } + + numericUIDs.push_back(numericUID); + } + + // Sort a copy of the list + std::vector <vmime_uint32> sortedUIDs; + + sortedUIDs.resize(numericUIDs.size()); + + std::copy(numericUIDs.begin(), numericUIDs.end(), sortedUIDs.begin()); + std::sort(sortedUIDs.begin(), sortedUIDs.end()); + + // Build the set by detecting ranges of continuous numbers + vmime_uint32 previous = static_cast <vmime_uint32>(-1), rangeStart = static_cast <vmime_uint32>(-1); + messageSet set; + + for (std::vector <vmime_uint32>::const_iterator it = sortedUIDs.begin() ; + it != sortedUIDs.end() ; ++it) { + + const vmime_uint32 current = *it; + + if (current == previous) { + continue; // skip duplicates + } + + if (previous == static_cast <vmime_uint32>(-1)) { + + previous = current; + rangeStart = current; + + } else { + + if (current == previous + 1) { + + previous = current; + + } else { + + set.m_ranges.push_back( + new UIDMessageRange( + utility::stringUtils::toString(rangeStart), + utility::stringUtils::toString(previous) + ) + ); + + previous = current; + rangeStart = current; + } + } + } + + set.m_ranges.push_back( + new UIDMessageRange( + utility::stringUtils::toString(rangeStart), + utility::stringUtils::toString(previous) + ) + ); + + return set; +} + + +void messageSet::addRange(const messageRange& range) { + + if (!m_ranges.empty() && typeid(*m_ranges[0]) != typeid(range)) { + throw std::invalid_argument("range"); + } + + m_ranges.push_back(range.clone()); +} + + +void messageSet::enumerate(messageSetEnumerator& en) const { + + for (size_t i = 0, n = m_ranges.size() ; i < n ; ++i) { + m_ranges[i]->enumerate(en); + } +} + + +bool messageSet::isEmpty() const { + + return m_ranges.empty(); +} + + +bool messageSet::isNumberSet() const { + + return !isEmpty() && dynamic_cast <numberMessageRange*>(m_ranges[0]) != NULL; +} + + +bool messageSet::isUIDSet() const { + + return !isEmpty() && dynamic_cast <UIDMessageRange*>(m_ranges[0]) != NULL; +} + + +size_t messageSet::getRangeCount() const { + + return m_ranges.size(); +} + + +const messageRange& messageSet::getRangeAt(const size_t i) const { + + return *m_ranges[i]; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/messageSet.hpp b/vmime-master/src/vmime/net/messageSet.hpp new file mode 100644 index 0000000..848c4c9 --- /dev/null +++ b/vmime-master/src/vmime/net/messageSet.hpp @@ -0,0 +1,358 @@ +// +// 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. +// + +#ifndef VMIME_NET_MESSAGESET_HPP_INCLUDED +#define VMIME_NET_MESSAGESET_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/message.hpp" + + +namespace vmime { +namespace net { + + +// Forward references +class numberMessageRange; +class UIDMessageRange; + + +/** Enumerator used to retrieve the message number/UID ranges contained + * in a messageSet object. + */ +class VMIME_EXPORT messageSetEnumerator { + +public: + virtual ~messageSetEnumerator(); + virtual void enumerateNumberMessageRange(const numberMessageRange& range) = 0; + virtual void enumerateUIDMessageRange(const UIDMessageRange& range) = 0; +}; + + +/** A range of (continuous) messages, designated either by their + * sequence number, or by their UID. + */ +class VMIME_EXPORT messageRange : public object { + +public: + + virtual ~messageRange(); + + /** Enumerates this range with the specified enumerator. + * + * @param en enumerator that will receive the method calls while + * enumerating this range + */ + virtual void enumerate(messageSetEnumerator& en) const = 0; + + /** Clones this message range. + */ + virtual messageRange* clone() const = 0; + +protected: + + messageRange(); + messageRange(const messageRange&); +}; + + +/** A range of (continuous) messages designated by their sequence number. + */ +class VMIME_EXPORT numberMessageRange : public messageRange { + +public: + + /** Constructs a message range containing a single message. + * + * @param number message number (numbering starts at 1, not 0) + */ + numberMessageRange(const size_t number); + + /** Constructs a message range for multiple messages. + * + * @param first number of the first message in the range (numbering + * starts at 1, not 0) + * @param last number of the last message in the range, or use the + * special value -1 to designate the last message in the folder + */ + numberMessageRange(const size_t first, const size_t last); + + /** Constructs a message range by copying from another range. + * + * @param other range to copy + */ + numberMessageRange(const numberMessageRange& other); + + /** Returns the number of the first message in the range. + * + * @return number of the first message + */ + size_t getFirst() const; + + /** Returns the number of the last message in the range, or -1 + * to designate the last message in the folder + * + * @return number of the last message + */ + size_t getLast() const; + + void enumerate(messageSetEnumerator& en) const; + + messageRange* clone() const; + +private: + + size_t m_first, m_last; +}; + + +/** A range of (continuous) messages represented by their UID. + */ +class VMIME_EXPORT UIDMessageRange : public messageRange { + +public: + + /** Constructs a message range containing a single message. + * + * @param uid message UID + */ + UIDMessageRange(const message::uid& uid); + + /** Constructs a message range for multiple messages. + * + * @param first UID of the first message in the range + * @param last UID of the last message in the range, or use the + * special value '*' to designate the last message in the folder + */ + UIDMessageRange(const message::uid& first, const message::uid& last); + + /** Constructs a message range by copying from another range. + * + * @param other range to copy + */ + UIDMessageRange(const UIDMessageRange& other); + + /** Returns the UID of the first message in the range. + * + * @return UID of the first message + */ + const message::uid getFirst() const; + + /** Returns the UID of the last message in the range, or '*' + * to designate the last message in the folder + * + * @return UID of the last message + */ + const message::uid getLast() const; + + void enumerate(messageSetEnumerator& en) const; + + messageRange* clone() const; + +private: + + message::uid m_first, m_last; +}; + + +/** Represents a set of messages, designated either by their sequence + * number, or by their UID (but not both). + * + * Following is example code to designate messages by their number: + * \code{.cpp} + * // Designate a single message with sequence number 42 + * vmime::net::messageSet::byNumber(42) + * + * // Designate messages from sequence number 5 to sequence number 8 (including) + * vmime::net::messageSet::byNumber(5, 8) + * + * // Designate all messages in the folder, starting from number 42 + * vmime::net::messageSet::byNumber(42, -1) + * \endcode + * Or, to designate messages by their UID, use: + * \code{.cpp} + * // Designate a single message with UID 1042 + * vmime::net::messageSet::byUID(1042) + * + * // Designate messages from UID 1000 to UID 1042 (including) + * vmime::net::messageSet::byUID(1000, 1042) + * + * // Designate all messages in the folder, starting from UID 1000 + * vmime::net::messageSet::byUID(1000, "*") + * \endcode + */ +class VMIME_EXPORT messageSet : public object { + +public: + + ~messageSet(); + + messageSet(const messageSet& other); + + /** Constructs an empty set. + * + * @return new empty message set + */ + static messageSet empty(); + + /** Constructs a new message set and initializes it with a single + * message represented by its sequence number. + * + * @param number message number (numbering starts at 1, not 0) + * @return new message set + */ + static messageSet byNumber(const size_t number); + + /** Constructs a new message set and initializes it with a range + * of messages represented by their sequence number. + * + * @param first number of the first message in the range (numbering + * starts at 1, not 0) + * @param last number of the last message in the range, or use the + * special value -1 to designate the last message in the folder + * @return new message set + */ + static messageSet byNumber(const size_t first, const size_t last); + + /** Constructs a new message set and initializes it with a possibly + * unsorted list of messages represented by their sequence number. + * Please note that numbering starts at 1, not 0. + * + * The function tries to group consecutive message numbers into + * ranges to reduce the size of the resulting set. + * + * For example, given the list "1,2,3,4,5,7,8,13,15,16,17" it will + * result in the following ranges: "1:5,7:8,13,15:17". + * + * @param numbers a vector containing numbers of the messages + * @return new message set + */ + static messageSet byNumber(const std::vector <size_t>& numbers); + + /** Constructs a new message set and initializes it with a single + * message represented by its UID. + * + * @param uid message UID + * @return new message set + */ + static messageSet byUID(const message::uid& uid); + + /** Constructs a new message set and initializes it with a range + * of messages represented by their sequence number. + * + * @param first UID of the first message in the range + * @param last UID of the last message in the range, or use the + * special value '*' to designate the last message in the folder + * @return new message set + */ + static messageSet byUID(const message::uid& first, const message::uid& last); + + /** Constructs a new message set and initializes it with a possibly + * unsorted list of messages represented by their UID. + * + * For UIDs that actually are numbers (this is the case for IMAP), the + * function tries to group consecutive UIDs into ranges to reduce the + * size of the resulting set. + * + * For example, given the list "1,2,3,4,5,7,8,13,15,16,17" it will + * result in the following ranges: "1:5,7:8,13,15:17". + * + * @param uids a vector containing UIDs of the messages + * @return new message set + */ + static messageSet byUID(const std::vector <message::uid>& uids); + + /** Adds the specified range to this set. The type of message range + * (either number or UID) must match the type of the ranges already + * contained in this set (ie. it's not possible to have a message + * set which contains both number ranges and UID ranges). + * + * @param range range to add + * @throw std::invalid_argument exception if the range type does + * not match the type of the ranges in this set + */ + void addRange(const messageRange& range); + + /** Enumerates this set with the specified enumerator. + * + * @param en enumerator that will receive the method calls while + * enumerating the ranges in this set + */ + void enumerate(messageSetEnumerator& en) const; + + /** Returns whether this set is empty (contains no range). + * + * @return true if this set is empty, or false otherwise + */ + bool isEmpty() const; + + /** Returns whether this set references messages by their sequence + * number. + * + * @return true if this set references messages by their sequence + * number, or false otherwise + */ + bool isNumberSet() const; + + /** Returns whether this set references messages by their UID. + * + * @return true if this set references messages by their UID, + * or false otherwise + */ + bool isUIDSet() const; + + /** Returns the number of ranges contained in this set. + * + * @return range count + */ + size_t getRangeCount() const; + + /** Returns the message range at the specified index. + * + * @param i range index (from 0 to getRangeCount()) + * @return a reference to the message range at the specified index + */ + const messageRange& getRangeAt(const size_t i) const; + +private: + + messageSet(); + + std::vector <messageRange*> m_ranges; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_NET_MESSAGESET_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Command.cpp b/vmime-master/src/vmime/net/pop3/POP3Command.cpp new file mode 100644 index 0000000..0e79888 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Command.cpp @@ -0,0 +1,267 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" +#include "vmime/net/pop3/POP3Store.hpp" + +#include "vmime/net/socket.hpp" + +#include "vmime/mailbox.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Command::POP3Command(const string& text, const string& traceText) + : m_text(text), + m_traceText(traceText) { + +} + + +// static +shared_ptr <POP3Command> POP3Command::CAPA() { + + return createCommand("CAPA"); +} + + +// static +shared_ptr <POP3Command> POP3Command::NOOP() { + + return createCommand("NOOP"); +} + + +// static +shared_ptr <POP3Command> POP3Command::AUTH(const string& mechName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::AUTH(const string& mechName, const string& initialResponse) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::STLS() { + + return createCommand("STLS"); +} + + +// static +shared_ptr <POP3Command> POP3Command::APOP(const string& username, const string& digest) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "APOP " << username << " " << digest; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::USER(const string& username) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "USER " << username; + + std::ostringstream trace; + trace.imbue(std::locale::classic()); + trace << "USER {username}"; + + return createCommand(cmd.str(), trace.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::PASS(const string& password) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "PASS " << password; + + std::ostringstream trace; + trace.imbue(std::locale::classic()); + trace << "PASS {password}"; + + return createCommand(cmd.str(), trace.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::STAT() { + + return createCommand("STAT"); +} + + +// static +shared_ptr <POP3Command> POP3Command::LIST() { + + return createCommand("LIST"); +} + + +// static +shared_ptr <POP3Command> POP3Command::LIST(const unsigned long msg) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "LIST " << msg; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::UIDL() { + + return createCommand("UIDL"); +} + + +// static +shared_ptr <POP3Command> POP3Command::UIDL(const unsigned long msg) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "UIDL " << msg; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::DELE(const unsigned long msg) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "DELE " << msg; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::RETR(const unsigned long msg) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RETR " << msg; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::TOP(const unsigned long msg, const unsigned long lines) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "TOP " << msg << " " << lines; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <POP3Command> POP3Command::RSET() { + + return createCommand("RSET"); +} + + +// static +shared_ptr <POP3Command> POP3Command::QUIT() { + + return createCommand("QUIT"); +} + + +// static +shared_ptr <POP3Command> POP3Command::createCommand( + const string& text, + const string& traceText +) { + + if (traceText.empty()) { + return shared_ptr <POP3Command>(new POP3Command(text, text)); + } else { + return shared_ptr <POP3Command>(new POP3Command(text, traceText)); + } +} + + +const string POP3Command::getText() const { + + return m_text; +} + + +const string POP3Command::getTraceText() const { + + return m_traceText; +} + + +void POP3Command::send(const shared_ptr <POP3Connection>& conn) { + + conn->getSocket()->send(m_text + "\r\n"); + + if (conn->getTracer()) { + conn->getTracer()->traceSend(m_traceText); + } +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/vmime-master/src/vmime/net/pop3/POP3Command.hpp b/vmime-master/src/vmime/net/pop3/POP3Command.hpp new file mode 100644 index 0000000..06a61b9 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Command.hpp @@ -0,0 +1,123 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3COMMAND_HPP_INCLUDED +#define VMIME_NET_POP3_POP3COMMAND_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" + + +namespace vmime { + + +class mailbox; + + +namespace net { +namespace pop3 { + + +class POP3Connection; + + +/** A POP3 command that will be sent to the server. + */ +class VMIME_EXPORT POP3Command : public object { + +public: + + static shared_ptr <POP3Command> CAPA(); + static shared_ptr <POP3Command> NOOP(); + static shared_ptr <POP3Command> AUTH(const string& mechName); + static shared_ptr <POP3Command> AUTH(const string& mechName, const string& initialResponse); + static shared_ptr <POP3Command> STLS(); + static shared_ptr <POP3Command> APOP(const string& username, const string& digest); + static shared_ptr <POP3Command> USER(const string& username); + static shared_ptr <POP3Command> PASS(const string& password); + static shared_ptr <POP3Command> STAT(); + static shared_ptr <POP3Command> LIST(); + static shared_ptr <POP3Command> LIST(const unsigned long msg); + static shared_ptr <POP3Command> UIDL(); + static shared_ptr <POP3Command> UIDL(const unsigned long msg); + static shared_ptr <POP3Command> DELE(const unsigned long msg); + static shared_ptr <POP3Command> RETR(const unsigned long msg); + static shared_ptr <POP3Command> TOP(const unsigned long msg, const unsigned long lines); + static shared_ptr <POP3Command> RSET(); + static shared_ptr <POP3Command> QUIT(); + + /** Creates a new POP3 command with the specified text. + * + * @param text command text + * @param traceText trace text (if empty, command text is used) + * @return a new POP3Command object + */ + static shared_ptr <POP3Command> createCommand(const string& text, const string& traceText = ""); + + /** Sends this command over the specified connection. + * + * @param conn connection onto which the command will be sent + */ + virtual void send(const shared_ptr <POP3Connection>& conn); + + /** Returns the full text of the command, including command name + * and parameters (if any). + * + * @return command text (eg. "LIST 42") + */ + virtual const string getText() const; + + /** Returns the full text of the command, suitable for outputing + * to the tracer. + * + * @return trace text (eg. "USER myusername") + */ + virtual const string getTraceText() const; + +protected: + + POP3Command(const string& text, const string& traceText); + POP3Command(const POP3Command&); + +private: + + string m_text; + string m_traceText; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3COMMAND_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Connection.cpp b/vmime-master/src/vmime/net/pop3/POP3Connection.cpp new file mode 100644 index 0000000..749f7ef --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Connection.cpp @@ -0,0 +1,737 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Connection.hpp" +#include "vmime/net/pop3/POP3Store.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/security/digest/messageDigestFactory.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/SASLContext.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" + #include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (m_store.lock()->getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const POP3ServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (m_store.lock()->getInfos().hasProperty(getSession(), \ + dynamic_cast <const POP3ServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace pop3 { + + + +POP3Connection::POP3Connection( + const shared_ptr <POP3Store>& store, + const shared_ptr <security::authenticator>& auth +) + : m_store(store), + m_auth(auth), + m_socket(null), + m_timeoutHandler(null), + m_authenticated(false), + m_secured(false), + m_capabilitiesFetched(false) { + + static int connectionId = 0; + + if (store->getTracerFactory()) { + m_tracer = store->getTracerFactory()->create(store, ++connectionId); + } +} + + +POP3Connection::~POP3Connection() { + + try { + + if (isConnected()) { + disconnect(); + } else if (m_socket) { + internalDisconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void POP3Connection::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS); + const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT); + + shared_ptr <POP3Store> store = m_store.lock(); + + // Create the time-out handler + if (store->getTimeoutHandlerFactory()) { + m_timeoutHandler = store->getTimeoutHandlerFactory()->create(); + } + + // Create and connect the socket + m_socket = store->getSocketFactory()->create(m_timeoutHandler); + m_socket->setTracer(m_tracer); + +#if VMIME_HAVE_TLS_SUPPORT + if (store->isPOP3S()) { // dedicated port/POP3S + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create + (store->getCertificateVerifier(), + store->getSession()->getTLSProperties()); + + shared_ptr <tls::TLSSocket> tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket); + } else +#endif // VMIME_HAVE_TLS_SUPPORT + { + m_cntInfos = make_shared <defaultConnectionInfos>(address, port); + } + + m_socket->connect(address, port); + + // Connection + // + // eg: C: <connection to server> + // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com> + + shared_ptr <POP3Response> response = POP3Response::readResponse( + dynamicCast <POP3Connection>(shared_from_this()) + ); + + if (!response->isSuccess()) { + + internalDisconnect(); + throw exceptions::connection_greeting_error(response->getFirstLine()); + } + +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!store->isPOP3S() && tls) { // only if not POP3S + + try { + + startTLS(); + + // Non-fatal error + } catch (exceptions::command_error&) { + + if (tlsRequired) { + throw; + } else { + // TLS is not required, so don't bother + } + + // Fatal error + } catch (...) { + throw; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Start authentication process + authenticate(messageId(response->getText())); +} + + +void POP3Connection::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + internalDisconnect(); +} + + +void POP3Connection::internalDisconnect() { + + if (m_socket) { + + if (m_socket->isConnected()) { + + try { + + POP3Command::QUIT()->send(dynamicCast <POP3Connection>(shared_from_this())); + POP3Response::readResponse(dynamicCast <POP3Connection>(shared_from_this())); + + } catch (exception&) { + + // Not important + } + + m_socket->disconnect(); + } + + m_socket = null; + } + + m_timeoutHandler = null; + + m_authenticated = false; + m_secured = false; + + m_cntInfos = null; +} + + +void POP3Connection::authenticate(const messageId& randomMID) { + + getAuthenticator()->setService(m_store.lock()); + +#if VMIME_HAVE_SASL_SUPPORT + // First, try SASL authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) { + + try { + + authenticateSASL(); + + m_authenticated = true; + return; + + } catch (exceptions::authentication_error&) { + + if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) { + + // Can't fallback on APOP/normal authentication + internalDisconnect(); + throw; + + } else { + + // Ignore, will try APOP/normal authentication + } + + } catch (exception&) { + + internalDisconnect(); + throw; + } + } +#endif // VMIME_HAVE_SASL_SUPPORT + + // Secured authentication with APOP (if requested and if available) + // + // eg: C: APOP vincent <digest> + // --- S: +OK vincent is a valid mailbox + + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); + + shared_ptr <POP3Connection> conn = dynamicCast <POP3Connection>(shared_from_this()); + shared_ptr <POP3Response> response; + + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) { + + if (randomMID.getLeft().length() != 0 && + randomMID.getRight().length() != 0) { + + // <digest> is the result of MD5 applied to "<message-id>password" + shared_ptr <security::digest::messageDigest> md5 = + security::digest::messageDigestFactory::getInstance()->create("md5"); + + md5->update(randomMID.generate() + password); + md5->finalize(); + + POP3Command::APOP(username, md5->getHexDigest())->send(conn); + response = POP3Response::readResponse(conn); + + if (response->isSuccess()) { + + m_authenticated = true; + return; + + } else { + + // Some servers close the connection after an unsuccessful APOP + // command, so the fallback may not always work... + // + // S: +OK Qpopper (version 4.0.5) at xxx starting. <30396.1126730747@xxx> + // C: APOP plop c5e0a87d088ec71d60e32692d4c5bdf4 + // S: -ERR [AUTH] Password supplied for "plop" is incorrect. + // S: +OK Pop server at xxx signing off. + // [Connection closed by foreign host.] + + if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK)) { + + // Can't fallback on basic authentication + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + + // Ensure connection is valid (cf. note above) + try { + + POP3Command::NOOP()->send(conn); + POP3Response::readResponse(conn); + + } catch (exceptions::socket_exception&) { + + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + } + + } else { + + // APOP not supported + if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK)) { + + // Can't fallback on basic authentication + internalDisconnect(); + throw exceptions::authentication_error("APOP not supported"); + } + } + } + + // Basic authentication + // + // eg: C: USER vincent + // --- S: +OK vincent is a valid mailbox + // + // C: PASS couic + // S: +OK vincent's maildrop has 2 messages (320 octets) + POP3Command::USER(username)->send(conn); + response = POP3Response::readResponse(conn); + + if (!response->isSuccess()) { + + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + + POP3Command::PASS(password)->send(conn); + response = POP3Response::readResponse(conn); + + if (!response->isSuccess()) { + + internalDisconnect(); + throw exceptions::authentication_error(response->getFirstLine()); + } + + m_authenticated = true; +} + + +#if VMIME_HAVE_SASL_SUPPORT + +void POP3Connection::authenticateSASL() { + + if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) { + throw exceptions::authentication_error("No SASL authenticator available."); + } + + std::vector <string> capa = getCapabilities(); + std::vector <string> saslMechs; + + for (unsigned int i = 0 ; i < capa.size() ; ++i) { + + const string& x = capa[i]; + + // C: CAPA + // S: +OK List of capabilities follows + // S: LOGIN-DELAY 0 + // S: PIPELINING + // S: UIDL + // S: ... + // S: SASL DIGEST-MD5 CRAM-MD5 <----- + // S: EXPIRE NEVER + // S: ... + + if (x.length() > 5 && + (x[0] == 'S' || x[0] == 's') && + (x[1] == 'A' || x[1] == 'a') && + (x[2] == 'S' || x[2] == 's') && + (x[3] == 'L' || x[3] == 'l') && + (x[4] == ' ' || x[4] == '\t')) { + + const string list(x.begin() + 5, x.end()); + + std::istringstream iss(list); + iss.imbue(std::locale::classic()); + + string mech; + + while (iss >> mech) { + saslMechs.push_back(mech); + } + } + } + + if (saslMechs.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; + + shared_ptr <security::sasl::SASLContext> saslContext = + security::sasl::SASLContext::create(); + + for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { + + try { + mechList.push_back(saslContext->createMechanism(saslMechs[i])); + } catch (exceptions::no_such_mechanism&) { + // Ignore mechanism + } + } + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try to suggest a mechanism among all those supported + shared_ptr <security::sasl::SASLMechanism> suggestedMech = + saslContext->suggestMechanism(mechList); + + if (!suggestedMech) { + throw exceptions::authentication_error("Unable to suggest SASL mechanism."); + } + + // Allow application to choose which mechanisms to use + mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> + getAcceptableMechanisms(mechList, suggestedMech); + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try each mechanism in the list in turn + for (unsigned int i = 0 ; i < mechList.size() ; ++i) { + + shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; + + shared_ptr <security::sasl::SASLSession> saslSession = + saslContext->createSession("pop3", getAuthenticator(), mech); + + saslSession->init(); + + shared_ptr <POP3Command> authCmd; + + if (saslSession->getMechanism()->hasInitialResponse()) { + + byte_t* initialResp = 0; + size_t initialRespLen = 0; + + saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); + + string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); + delete [] initialResp; + + if (encodedInitialResp.empty()) { + authCmd = POP3Command::AUTH(mech->getName(), "="); + } else { + authCmd = POP3Command::AUTH(mech->getName(), encodedInitialResp); + } + + } else { + + authCmd = POP3Command::AUTH(mech->getName()); + } + + authCmd->send(dynamicCast <POP3Connection>(shared_from_this())); + + for (bool cont = true ; cont ; ) { + + shared_ptr <POP3Response> response = + POP3Response::readResponse(dynamicCast <POP3Connection>(shared_from_this())); + + switch (response->getCode()) { + + case POP3Response::CODE_OK: { + + m_socket = saslSession->getSecuredSocket(m_socket); + return; + } + + case POP3Response::CODE_READY: { + + byte_t* challenge = 0; + size_t challengeLen = 0; + + byte_t* resp = 0; + size_t respLen = 0; + + try { + + // Extract challenge + saslContext->decodeB64(response->getText(), &challenge, &challengeLen); + + // Prepare response + saslSession->evaluateChallenge(challenge, challengeLen, &resp, &respLen); + + // Send response + const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; + m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); + + if (m_tracer) { + m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); + } + + } catch (exceptions::sasl_exception& e) { + + if (challenge) { + delete [] challenge; + challenge = NULL; + } + + if (resp) { + delete [] resp; + resp = NULL; + } + + // Cancel SASL exchange + m_socket->send("*\r\n"); + + if (m_tracer) { + m_tracer->traceSend("*"); + } + + } catch (...) { + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + throw; + } + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + break; + } + + default: + + cont = false; + break; + } + } + } + + throw exceptions::authentication_error("Could not authenticate using SASL: all mechanisms failed."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +#if VMIME_HAVE_TLS_SUPPORT + +void POP3Connection::startTLS() { + + try { + + POP3Command::STLS()->send(dynamicCast <POP3Connection>(shared_from_this())); + + shared_ptr <POP3Response> response = + POP3Response::readResponse(dynamicCast <POP3Connection>(shared_from_this())); + + if (!response->isSuccess()) { + throw exceptions::command_error("STLS", response->getFirstLine()); + } + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + m_store.lock()->getCertificateVerifier(), + m_store.lock()->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + tlsSocket->handshake(); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>( + m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket + ); + + // " Once TLS has been started, the client MUST discard cached + // information about server capabilities and SHOULD re-issue + // the CAPA command. This is necessary to protect against + // man-in-the-middle attacks which alter the capabilities list + // prior to STLS. " (RFC-2595) + invalidateCapabilities(); + + } catch (exceptions::command_error&) { + + // Non-fatal error + throw; + + } catch (exception&) { + + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +const std::vector <string> POP3Connection::getCapabilities() { + + if (!m_capabilitiesFetched) { + fetchCapabilities(); + } + + return m_capabilities; +} + + +void POP3Connection::invalidateCapabilities() { + + m_capabilities.clear(); + m_capabilitiesFetched = false; +} + + +void POP3Connection::fetchCapabilities() { + + POP3Command::CAPA()->send(dynamicCast <POP3Connection>(shared_from_this())); + + shared_ptr <POP3Response> response = + POP3Response::readMultilineResponse(dynamicCast <POP3Connection>(shared_from_this())); + + std::vector <string> res; + + if (response->isSuccess()) { + + for (size_t i = 0, n = response->getLineCount() ; i < n ; ++i) { + res.push_back(response->getLineAt(i)); + } + } + + m_capabilities = res; + m_capabilitiesFetched = true; +} + + +bool POP3Connection::isConnected() const { + + return m_socket && m_socket->isConnected() && m_authenticated; +} + + +bool POP3Connection::isSecuredConnection() const { + + return m_secured; +} + + +shared_ptr <connectionInfos> POP3Connection::getConnectionInfos() const { + + return m_cntInfos; +} + + +shared_ptr <POP3Store> POP3Connection::getStore() { + + return m_store.lock(); +} + + +shared_ptr <session> POP3Connection::getSession() { + + return m_store.lock()->getSession(); +} + + +shared_ptr <socket> POP3Connection::getSocket() { + + return m_socket; +} + + +shared_ptr <tracer> POP3Connection::getTracer() { + + return m_tracer; +} + + +shared_ptr <timeoutHandler> POP3Connection::getTimeoutHandler() { + + return m_timeoutHandler; +} + + +shared_ptr <security::authenticator> POP3Connection::getAuthenticator() { + + return m_auth; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/vmime-master/src/vmime/net/pop3/POP3Connection.hpp b/vmime-master/src/vmime/net/pop3/POP3Connection.hpp new file mode 100644 index 0000000..26b3f3c --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Connection.hpp @@ -0,0 +1,132 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3CONNECTION_HPP_INCLUDED +#define VMIME_NET_POP3_POP3CONNECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/messageId.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/session.hpp" +#include "vmime/net/connectionInfos.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Response.hpp" + +#include "vmime/security/authenticator.hpp" + + +namespace vmime { +namespace net { + + +class socket; +class timeoutHandler; + + +namespace pop3 { + + +class POP3Store; + + +/** Manage connection to a POP3 server. + */ +class VMIME_EXPORT POP3Connection : public object, public enable_shared_from_this <POP3Connection> { + +public: + + POP3Connection( + const shared_ptr <POP3Store>& store, + const shared_ptr <security::authenticator>& auth + ); + + virtual ~POP3Connection(); + + + virtual void connect(); + virtual bool isConnected() const; + virtual void disconnect(); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + + virtual shared_ptr <POP3Store> getStore(); + virtual shared_ptr <socket> getSocket(); + virtual shared_ptr <timeoutHandler> getTimeoutHandler(); + virtual shared_ptr <security::authenticator> getAuthenticator(); + virtual shared_ptr <session> getSession(); + virtual shared_ptr <tracer> getTracer(); + +private: + + void authenticate(const messageId& randomMID); +#if VMIME_HAVE_SASL_SUPPORT + void authenticateSASL(); +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + + void fetchCapabilities(); + void invalidateCapabilities(); + const std::vector <string> getCapabilities(); + + void internalDisconnect(); + + + weak_ptr <POP3Store> m_store; + + shared_ptr <security::authenticator> m_auth; + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + bool m_authenticated; + bool m_secured; + + shared_ptr <connectionInfos> m_cntInfos; + + std::vector <string> m_capabilities; + bool m_capabilitiesFetched; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3CONNECTION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Folder.cpp b/vmime-master/src/vmime/net/pop3/POP3Folder.cpp new file mode 100644 index 0000000..b69a483 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Folder.cpp @@ -0,0 +1,822 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Folder.hpp" + +#include "vmime/net/pop3/POP3Store.hpp" +#include "vmime/net/pop3/POP3Message.hpp" +#include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3FolderStatus.hpp" + +#include "vmime/net/pop3/POP3Utils.hpp" + +#include "vmime/exception.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Folder::POP3Folder( + const folder::path& path, + const shared_ptr <POP3Store>& store +) + : m_store(store), + m_path(path), + m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()), + m_mode(-1), + m_open(false) { + + store->registerFolder(this); +} + + +POP3Folder::~POP3Folder() { + + try { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (store) { + + if (m_open) { + close(false); + } + + store->unregisterFolder(this); + + } else if (m_open) { + + onClose(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +int POP3Folder::getMode() const { + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_mode; +} + + +const folderAttributes POP3Folder::getAttributes() { + + folderAttributes attribs; + + if (m_path.isEmpty()) { + attribs.setType(folderAttributes::TYPE_CONTAINS_FOLDERS); + } else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX") { + attribs.setType(folderAttributes::TYPE_CONTAINS_MESSAGES); + attribs.setSpecialUse(folderAttributes::SPECIALUSE_INBOX); + } else { + throw exceptions::folder_not_found(); + } + + attribs.setFlags(0); + + return attribs; +} + + +const folder::path::component POP3Folder::getName() const { + + return m_name; +} + + +const folder::path POP3Folder::getFullPath() const { + + return m_path; +} + + +void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (m_path.isEmpty()) { + + if (mode != MODE_READ_ONLY && failIfModeIsNotAvailable) { + throw exceptions::operation_not_supported(); + } + + m_open = true; + m_mode = mode; + + m_messageCount = 0; + + } else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX") { + + POP3Command::STAT()->send(store->getConnection()); + + shared_ptr <POP3Response> response = POP3Response::readResponse(store->getConnection()); + + if (!response->isSuccess()) { + throw exceptions::command_error("STAT", response->getFirstLine()); + } + + std::istringstream iss(response->getText()); + iss.imbue(std::locale::classic()); + iss >> m_messageCount; + + if (iss.fail()) { + throw exceptions::invalid_response("STAT", response->getFirstLine()); + } + + m_open = true; + m_mode = mode; + + } else { + + throw exceptions::folder_not_found(); + } +} + + +void POP3Folder::close(const bool expunge) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (!expunge) { + POP3Command::RSET()->send(store->getConnection()); + POP3Response::readResponse(store->getConnection()); + } + + m_open = false; + m_mode = -1; + + onClose(); +} + + +void POP3Folder::onClose() { + + for (MessageMap::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) { + (*it).first->onFolderClosed(); + } + + m_messages.clear(); +} + + +void POP3Folder::create(const folderAttributes& /* attribs */) { + + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::destroy() { + + throw exceptions::operation_not_supported(); +} + + +bool POP3Folder::exists() { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + return m_path.isEmpty() || (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX"); +} + + +bool POP3Folder::isOpen() const { + + return m_open; +} + + +shared_ptr <message> POP3Folder::getMessage(const size_t num) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } else if (num < 1 || num > m_messageCount) { + throw exceptions::message_not_found(); + } + + return make_shared <POP3Message>(dynamicCast <POP3Folder>(shared_from_this()), num); +} + + +std::vector <shared_ptr <message> > POP3Folder::getMessages(const messageSet& msgs) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msgs.isNumberSet()) { + + const std::vector <size_t> numbers = POP3Utils::messageSetToNumberList(msgs, m_messageCount); + + std::vector <shared_ptr <message> > messages; + shared_ptr <POP3Folder> thisFolder(dynamicCast <POP3Folder>(shared_from_this())); + + for (std::vector <size_t>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it) { + + if (*it < 1|| *it > m_messageCount) { + throw exceptions::message_not_found(); + } + + messages.push_back(make_shared <POP3Message>(thisFolder, *it)); + } + + return messages; + + } else { + + throw exceptions::operation_not_supported(); + } +} + + +size_t POP3Folder::getMessageCount() { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + return m_messageCount; +} + + +shared_ptr <folder> POP3Folder::getFolder(const folder::path::component& name) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + return shared_ptr <POP3Folder>(new POP3Folder(m_path / name, store)); +} + + +std::vector <shared_ptr <folder> > POP3Folder::getFolders(const bool /* recursive */) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (m_path.isEmpty()) { + + std::vector <shared_ptr <folder> > v; + v.push_back(shared_ptr <POP3Folder>(new POP3Folder(folder::path::component("INBOX"), store))); + return v; + + } else { + + std::vector <shared_ptr <folder> > v; + return v; + } +} + + +void POP3Folder::fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress +) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + if (msg.empty()) { + return; + } + + const size_t total = msg.size(); + size_t current = 0; + + if (progress) { + progress->start(total); + } + + for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; + it != msg.end() ; ++it) { + + dynamicCast <POP3Message>(*it)->fetch( + dynamicCast <POP3Folder>(shared_from_this()), + options + ); + + if (progress) { + progress->progress(++current, total); + } + } + + if (options.has(fetchAttributes::SIZE)) { + + // Send the "LIST" command + POP3Command::LIST()->send(store->getConnection()); + + // Get the response + shared_ptr <POP3Response> response = + POP3Response::readMultilineResponse(store->getConnection()); + + if (response->isSuccess()) { + + // C: LIST + // S: +OK + // S: 1 47548 + // S: 2 12653 + // S: . + std::map <size_t, string> result; + POP3Utils::parseMultiListOrUidlResponse(response, result); + + for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; + it != msg.end() ; ++it) { + + shared_ptr <POP3Message> m = dynamicCast <POP3Message>(*it); + + std::map <size_t, string>::const_iterator x = result.find(m->m_num); + + if (x != result.end()) { + + size_t size = 0; + + std::istringstream iss((*x).second); + iss.imbue(std::locale::classic()); + iss >> size; + + m->m_size = size; + } + } + } + } + + if (options.has(fetchAttributes::UID)) { + + // Send the "UIDL" command + POP3Command::UIDL()->send(store->getConnection()); + + // Get the response + shared_ptr <POP3Response> response = + POP3Response::readMultilineResponse(store->getConnection()); + + if (response->isSuccess()) { + + // C: UIDL + // S: +OK + // S: 1 whqtswO00WBw418f9t5JxYwZ + // S: 2 QhdPYR:00WBw1Ph7x7 + // S: . + std::map <size_t, string> result; + POP3Utils::parseMultiListOrUidlResponse(response, result); + + for (std::vector <shared_ptr <message> >::iterator it = msg.begin() ; + it != msg.end() ; ++it) { + + shared_ptr <POP3Message> m = dynamicCast <POP3Message>(*it); + + std::map <size_t, string>::const_iterator x = result.find(m->m_num); + + if (x != result.end()) { + m->m_uid = (*x).second; + } + } + } + } + + if (progress) { + progress->stop(total); + } +} + + +void POP3Folder::fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options) { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + dynamicCast <POP3Message>(msg)->fetch( + dynamicCast <POP3Folder>(shared_from_this()), + options + ); + + if (options.has(fetchAttributes::SIZE)) { + + // Send the "LIST" command + POP3Command::LIST(msg->getNumber())->send(store->getConnection()); + + // Get the response + shared_ptr <POP3Response> response = + POP3Response::readResponse(store->getConnection()); + + if (response->isSuccess()) { + + string responseText = response->getText(); + + // C: LIST 2 + // S: +OK 2 4242 + string::iterator it = responseText.begin(); + + while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; + while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != responseText.end()) { + + size_t size = 0; + + std::istringstream iss(string(it, responseText.end())); + iss.imbue(std::locale::classic()); + iss >> size; + + dynamicCast <POP3Message>(msg)->m_size = size; + } + } + } + + if (options.has(fetchAttributes::UID)) { + + // Send the "UIDL" command + POP3Command::UIDL(msg->getNumber())->send(store->getConnection()); + + // Get the response + shared_ptr <POP3Response> response = + POP3Response::readResponse(store->getConnection()); + + if (response->isSuccess()) { + + string responseText = response->getText(); + + // C: UIDL 2 + // S: +OK 2 QhdPYR:00WBw1Ph7x7 + string::iterator it = responseText.begin(); + + while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; + while (it != responseText.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != responseText.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != responseText.end()) { + dynamicCast <POP3Message>(msg)->m_uid = string(it, responseText.end()); + } + } + } +} + + +std::vector <shared_ptr <message> > POP3Folder::getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs +) { + + if (msgs.isEmpty()) { + return std::vector <shared_ptr <message> >(); + } + + std::vector <shared_ptr <message> > messages = getMessages(msgs); + fetchMessages(messages, attribs); + + return messages; +} + + +int POP3Folder::getFetchCapabilities() const { + + return fetchAttributes::ENVELOPE | + fetchAttributes::CONTENT_INFO | + fetchAttributes::SIZE | + fetchAttributes::FULL_HEADER | + fetchAttributes::UID | + fetchAttributes::IMPORTANCE; +} + + +shared_ptr <folder> POP3Folder::getParent() { + + if (m_path.isEmpty()) { + return null; + } else { + return shared_ptr <POP3Folder>(new POP3Folder(m_path.getParent(), m_store.lock())); + } +} + + +shared_ptr <const store> POP3Folder::getStore() const { + + return m_store.lock(); +} + + +shared_ptr <store> POP3Folder::getStore() { + + return m_store.lock(); +} + + +void POP3Folder::registerMessage(POP3Message* msg) { + + m_messages.insert(MessageMap::value_type(msg, msg->getNumber())); +} + + +void POP3Folder::unregisterMessage(POP3Message* msg) { + + m_messages.erase(msg); +} + + +void POP3Folder::onStoreDisconnected() { + + m_store.reset(); +} + + +void POP3Folder::deleteMessages(const messageSet& msgs) { + + shared_ptr <POP3Store> store = m_store.lock(); + + const std::vector <size_t> nums = POP3Utils::messageSetToNumberList(msgs, m_messageCount); + + if (nums.empty()) { + throw exceptions::invalid_argument(); + } + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } else if (!isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + + for (std::vector <size_t>::const_iterator + it = nums.begin() ; it != nums.end() ; ++it) { + + POP3Command::DELE(*it)->send(store->getConnection()); + + shared_ptr <POP3Response> response = + POP3Response::readResponse(store->getConnection()); + + if (!response->isSuccess()) { + throw exceptions::command_error("DELE", response->getFirstLine()); + } + } + + // Sort message list + std::vector <size_t> list; + + list.resize(nums.size()); + std::copy(nums.begin(), nums.end(), list.begin()); + + std::sort(list.begin(), list.end()); + + // Update local flags + for (std::map <POP3Message*, size_t>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) { + + POP3Message* msg = (*it).first; + + if (std::binary_search(list.begin(), list.end(), msg->getNumber())) { + msg->m_deleted = true; + } + } + + // Notify message flags changed + shared_ptr <events::messageChangedEvent> event = + make_shared <events::messageChangedEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageChangedEvent::TYPE_FLAGS, + list + ); + + notifyMessageChanged(event); +} + + +void POP3Folder::setMessageFlags( + const messageSet& /* msgs */, + const int /* flags */, + const int /* mode */ +) { + + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::rename(const folder::path& /* newPath */) { + + throw exceptions::operation_not_supported(); +} + + +messageSet POP3Folder::addMessage( + const shared_ptr <vmime::message>& /* msg */, + const int /* flags */, + vmime::datetime* /* date */, + utility::progressListener* /* progress */ +) { + + throw exceptions::operation_not_supported(); +} + + +messageSet POP3Folder::addMessage( + utility::inputStream& /* is */, + const size_t /* size */, + const int /* flags */, + vmime::datetime* /* date */, + utility::progressListener* /* progress */ +) { + + throw exceptions::operation_not_supported(); +} + + +messageSet POP3Folder::copyMessages( + const folder::path& /* dest */, + const messageSet& /* msgs */ +) { + + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::status(size_t& count, size_t& unseen) { + + count = 0; + unseen = 0; + + shared_ptr <folderStatus> status = getStatus(); + + count = status->getMessageCount(); + unseen = status->getUnseenCount(); + + m_messageCount = count; +} + + +shared_ptr <folderStatus> POP3Folder::getStatus() { + + shared_ptr <POP3Store> store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store disconnected"); + } + + POP3Command::STAT()->send(store->getConnection()); + + shared_ptr <POP3Response> response = + POP3Response::readResponse(store->getConnection()); + + if (!response->isSuccess()) { + throw exceptions::command_error("STAT", response->getFirstLine()); + } + + + size_t count = 0; + + std::istringstream iss(response->getText()); + iss.imbue(std::locale::classic()); + iss >> count; + + shared_ptr <POP3FolderStatus> status = make_shared <POP3FolderStatus>(); + + status->setMessageCount(count); + status->setUnseenCount(count); + + // Update local message count + if (m_messageCount != count) { + + const size_t oldCount = m_messageCount; + + m_messageCount = count; + + if (count > oldCount) { + + std::vector <size_t> nums; + nums.resize(count - oldCount); + + for (size_t i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) { + nums[j] = i; + } + + // Notify message count changed + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>(shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + notifyMessageCount(event); + + // Notify folders with the same path + for (std::list <POP3Folder*>::iterator it = store->m_folders.begin() ; + it != store->m_folders.end() ; ++it) { + + if ((*it) != this && (*it)->getFullPath() == m_path) { + + (*it)->m_messageCount = count; + + shared_ptr <events::messageCountEvent> event = + make_shared <events::messageCountEvent>( + dynamicCast <folder>((*it)->shared_from_this()), + events::messageCountEvent::TYPE_ADDED, + nums + ); + + (*it)->notifyMessageCount(event); + } + } + } + } + + return status; +} + + +void POP3Folder::expunge() { + + // Not supported by POP3 protocol (deleted messages are automatically + // expunged at the end of the session...). +} + + +std::vector <size_t> POP3Folder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) { + + throw exceptions::operation_not_supported(); +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3Folder.hpp b/vmime-master/src/vmime/net/pop3/POP3Folder.hpp new file mode 100644 index 0000000..73e29f9 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Folder.hpp @@ -0,0 +1,179 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3FOLDER_HPP_INCLUDED +#define VMIME_NET_POP3_POP3FOLDER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include <vector> +#include <map> + +#include "vmime/types.hpp" + +#include "vmime/net/folder.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +class POP3Store; +class POP3Message; + + +/** POP3 folder implementation. + */ +class VMIME_EXPORT POP3Folder : public folder { + +private: + + friend class POP3Store; + friend class POP3Message; + + POP3Folder(const POP3Folder&); + POP3Folder(const folder::path& path, const shared_ptr <POP3Store>& store); + +public: + + ~POP3Folder(); + + int getMode() const; + + const folderAttributes getAttributes(); + + const folder::path::component getName() const; + const folder::path getFullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const folderAttributes& attribs); + + bool exists(); + + void destroy(); + + bool isOpen() const; + + shared_ptr <message> getMessage(const size_t num); + std::vector <shared_ptr <message> > getMessages(const messageSet& msgs); + + size_t getMessageCount(); + + shared_ptr <folder> getFolder(const folder::path::component& name); + std::vector <shared_ptr <folder> > getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessages(const messageSet& msgs); + + void setMessageFlags( + const messageSet& msgs, + const int flags, + const int mode = message::FLAG_MODE_SET + ); + + messageSet addMessage( + const shared_ptr <vmime::message>& msg, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet addMessage( + utility::inputStream& is, + const size_t size, + const int flags = -1, + vmime::datetime* date = NULL, + utility::progressListener* progress = NULL + ); + + messageSet copyMessages(const folder::path& dest, const messageSet& msgs); + + void status(size_t& count, size_t& unseen); + shared_ptr <folderStatus> getStatus(); + + void expunge(); + + shared_ptr <folder> getParent(); + + shared_ptr <const store> getStore() const; + shared_ptr <store> getStore(); + + + void fetchMessages( + std::vector <shared_ptr <message> >& msg, + const fetchAttributes& options, + utility::progressListener* progress = NULL + ); + + void fetchMessage(const shared_ptr <message>& msg, const fetchAttributes& options); + + std::vector <shared_ptr <message> > getAndFetchMessages( + const messageSet& msgs, + const fetchAttributes& attribs + ); + + int getFetchCapabilities() const; + + std::vector <size_t> getMessageNumbersStartingOnUID(const message::uid& uid); + +private: + + void registerMessage(POP3Message* msg); + void unregisterMessage(POP3Message* msg); + + void onStoreDisconnected(); + + void onClose(); + + + weak_ptr <POP3Store> m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + size_t m_messageCount; + + typedef std::map <POP3Message*, size_t> MessageMap; + MessageMap m_messages; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3FOLDER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3FolderStatus.cpp b/vmime-master/src/vmime/net/pop3/POP3FolderStatus.cpp new file mode 100644 index 0000000..9f2c49f --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3FolderStatus.cpp @@ -0,0 +1,88 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3FolderStatus.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3FolderStatus::POP3FolderStatus() + : m_count(0), + m_unseen(0) { + +} + + +POP3FolderStatus::POP3FolderStatus(const POP3FolderStatus& other) + : folderStatus(), + m_count(other.m_count), + m_unseen(other.m_unseen) { + +} + + +size_t POP3FolderStatus::getMessageCount() const { + + return m_count; +} + + +size_t POP3FolderStatus::getUnseenCount() const { + + return m_unseen; +} + + +void POP3FolderStatus::setMessageCount(const size_t count) { + + m_count = count; +} + + +void POP3FolderStatus::setUnseenCount(const size_t unseen) { + + m_unseen = unseen; +} + + +shared_ptr <folderStatus> POP3FolderStatus::clone() const { + + return make_shared <POP3FolderStatus>(*this); +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/vmime-master/src/vmime/net/pop3/POP3FolderStatus.hpp b/vmime-master/src/vmime/net/pop3/POP3FolderStatus.hpp new file mode 100644 index 0000000..0ce413e --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3FolderStatus.hpp @@ -0,0 +1,75 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED +#define VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/folderStatus.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** Holds the status of a POP3 folder. + */ +class VMIME_EXPORT POP3FolderStatus : public folderStatus { + +public: + + POP3FolderStatus(); + POP3FolderStatus(const POP3FolderStatus& other); + + // Inherited from folderStatus + size_t getMessageCount() const; + size_t getUnseenCount() const; + + shared_ptr <folderStatus> clone() const; + + + void setMessageCount(const size_t count); + void setUnseenCount(const size_t unseen); + +private: + + size_t m_count; + size_t m_unseen; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3FOLDERSTATUS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Message.cpp b/vmime-master/src/vmime/net/pop3/POP3Message.cpp new file mode 100644 index 0000000..8d6b7f5 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Message.cpp @@ -0,0 +1,283 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Message.hpp" +#include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3Folder.hpp" +#include "vmime/net/pop3/POP3Store.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" + +#include <sstream> + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Message::POP3Message( + const shared_ptr <POP3Folder>& folder, + const size_t num +) + : m_folder(folder), + m_num(num), + m_size(-1), + m_deleted(false) { + + folder->registerMessage(this); +} + + +POP3Message::~POP3Message() { + + try { + + shared_ptr <POP3Folder> folder = m_folder.lock(); + + if (folder) { + folder->unregisterMessage(this); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void POP3Message::onFolderClosed() { + + m_folder.reset(); +} + + +size_t POP3Message::getNumber() const { + + return m_num; +} + + +const message::uid POP3Message::getUID() const { + + return m_uid; +} + + +size_t POP3Message::getSize() const { + + if (m_size == static_cast <size_t>(-1)) { + throw exceptions::unfetched_object(); + } + + return m_size; +} + + +bool POP3Message::isExpunged() const { + + return false; +} + + +int POP3Message::getFlags() const { + + int flags = 0; + + if (m_deleted) { + flags |= FLAG_DELETED; + } + + return flags; +} + + +shared_ptr <const messageStructure> POP3Message::getStructure() const { + + throw exceptions::operation_not_supported(); +} + + +shared_ptr <messageStructure> POP3Message::getStructure() { + + throw exceptions::operation_not_supported(); +} + + +shared_ptr <const header> POP3Message::getHeader() const { + + if (!m_header) { + throw exceptions::unfetched_object(); + } + + return m_header; +} + + +void POP3Message::extract( + utility::outputStream& os, + utility::progressListener* progress, + const size_t start, + const size_t length, + const bool /* peek */ +) const { + + shared_ptr <const POP3Folder> folder = m_folder.lock(); + + if (!folder) { + throw exceptions::illegal_state("Folder closed"); + } else if (!folder->getStore()) { + throw exceptions::illegal_state("Store disconnected"); + } + + if (start != 0 && length != static_cast <size_t>(-1)) { + throw exceptions::partial_fetch_not_supported(); + } + + // Emit the "RETR" command + shared_ptr <POP3Store> store = folder->m_store.lock(); + + POP3Command::RETR(m_num)->send(store->getConnection()); + + try { + + POP3Response::readLargeResponse( + store->getConnection(), os, progress, + m_size == static_cast <size_t>(-1) ? 0 : m_size + ); + + } catch (exceptions::command_error& e) { + + throw exceptions::command_error("RETR", e.response()); + } +} + + +void POP3Message::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 { + + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetchPartHeader(const shared_ptr <messagePart>& /* p */) { + + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetch( + const shared_ptr <POP3Folder>& msgFolder, + const fetchAttributes& options +) { + + shared_ptr <POP3Folder> folder = m_folder.lock(); + + if (folder != msgFolder) { + throw exceptions::folder_not_found(); + } + + // STRUCTURE and FLAGS attributes are not supported by POP3 + if (options.has(fetchAttributes::STRUCTURE | fetchAttributes::FLAGS)) { + throw exceptions::operation_not_supported(); + } + + // Check for the real need to fetch the full header + static const int optionsRequiringHeader = + fetchAttributes::ENVELOPE | fetchAttributes::CONTENT_INFO | + fetchAttributes::FULL_HEADER | fetchAttributes::IMPORTANCE; + + if (!options.has(optionsRequiringHeader)) { + return; + } + + // No need to differenciate between ENVELOPE, CONTENT_INFO, ... + // since POP3 only permits to retrieve the whole header and not + // fields in particular. + + // Emit the "TOP" command + shared_ptr <POP3Store> store = folder->m_store.lock(); + + POP3Command::TOP(m_num, 0)->send(store->getConnection()); + + try { + + string buffer; + utility::outputStreamStringAdapter bufferStream(buffer); + + POP3Response::readLargeResponse( + store->getConnection(), + bufferStream, /* progress */ NULL, /* predictedSize */ 0 + ); + + m_header = make_shared <header>(); + m_header->parse(buffer); + + } catch (exceptions::command_error& e) { + + throw exceptions::command_error("TOP", e.response()); + } +} + + +void POP3Message::setFlags(const int /* flags */, const int /* mode */) { + + throw exceptions::operation_not_supported(); +} + + +shared_ptr <vmime::message> POP3Message::getParsedMessage() { + + std::ostringstream oss; + utility::outputStreamAdapter os(oss); + + extract(os); + + shared_ptr <vmime::message> msg = make_shared <vmime::message>(); + msg->parse(oss.str()); + + return msg; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3Message.hpp b/vmime-master/src/vmime/net/pop3/POP3Message.hpp new file mode 100644 index 0000000..3d6dc92 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Message.hpp @@ -0,0 +1,124 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED +#define VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/message.hpp" +#include "vmime/net/folder.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +class POP3Folder; + + +/** POP3 message implementation. + */ +class VMIME_EXPORT POP3Message : public message { + +private: + + friend class POP3Folder; + + POP3Message(const POP3Message&); + +public: + + POP3Message(const shared_ptr <POP3Folder>& folder, const size_t num); + + ~POP3Message(); + + + size_t getNumber() const; + + const uid getUID() const; + + size_t getSize() const; + + bool isExpunged() const; + + shared_ptr <const messageStructure> getStructure() const; + shared_ptr <messageStructure> getStructure(); + + shared_ptr <const header> getHeader() const; + + int getFlags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract( + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void extractPart( + const shared_ptr <const messagePart>& p, + utility::outputStream& os, + utility::progressListener* progress = NULL, + const size_t start = 0, + const size_t length = -1, + const bool peek = false + ) const; + + void fetchPartHeader(const shared_ptr <messagePart>& p); + + shared_ptr <vmime::message> getParsedMessage(); + +private: + + void fetch(const shared_ptr <POP3Folder>& folder, const fetchAttributes& options); + + void onFolderClosed(); + + weak_ptr <POP3Folder> m_folder; + size_t m_num; + uid m_uid; + size_t m_size; + + bool m_deleted; + + shared_ptr <header> m_header; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Response.cpp b/vmime-master/src/vmime/net/pop3/POP3Response.cpp new file mode 100644 index 0000000..725841e --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Response.cpp @@ -0,0 +1,504 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/inputStreamSocketAdapter.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Response::POP3Response( + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const shared_ptr <tracer>& tracer +) + : m_socket(sok), + m_timeoutHandler(toh), + m_tracer(tracer) { + +} + + +// static +shared_ptr <POP3Response> POP3Response::readResponse( + const shared_ptr <POP3Connection>& conn +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string buffer; + resp->readResponseImpl(buffer, /* multiLine */ false); + + resp->m_firstLine = buffer; + resp->m_code = getResponseCode(buffer); + stripResponseCode(buffer, resp->m_text); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(buffer); + } + + return resp; +} + + +// static +shared_ptr <POP3Response> POP3Response::readMultilineResponse( + const shared_ptr <POP3Connection>& conn +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string buffer; + resp->readResponseImpl(buffer, /* multiLine */ true); + + string firstLine, nextLines; + stripFirstLine(buffer, nextLines, &firstLine); + + resp->m_firstLine = firstLine; + resp->m_code = getResponseCode(firstLine); + stripResponseCode(firstLine, resp->m_text); + + std::istringstream iss(nextLines); + string line; + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(firstLine); + } + + while (std::getline(iss, line, '\n')) { + + line = utility::stringUtils::trim(line); + resp->m_lines.push_back(line); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(line); + } + } + + if (resp->m_tracer) { + resp->m_tracer->traceReceive("."); + } + + return resp; +} + + +// static +shared_ptr <POP3Response> POP3Response::readLargeResponse( + const shared_ptr <POP3Connection>& conn, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string firstLine; + const size_t length = resp->readResponseImpl(firstLine, os, progress, predictedSize); + + resp->m_firstLine = firstLine; + resp->m_code = getResponseCode(firstLine); + stripResponseCode(firstLine, resp->m_text); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(firstLine); + resp->m_tracer->traceReceiveBytes(length - firstLine.length()); + resp->m_tracer->traceReceive("."); + } + + return resp; +} + + +bool POP3Response::isSuccess() const { + + return m_code == CODE_OK; +} + + +const string POP3Response::getFirstLine() const { + + return m_firstLine; +} + + +POP3Response::ResponseCode POP3Response::getCode() const { + + return m_code; +} + + +const string POP3Response::getText() const { + + return m_text; +} + + +const string POP3Response::getLineAt(const size_t pos) const { + + return m_lines[pos]; +} + + +size_t POP3Response::getLineCount() const { + + return m_lines.size(); +} + + +void POP3Response::readResponseImpl(string& buffer, const bool multiLine) { + + bool foundTerminator = false; + + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + buffer.clear(); + + char last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) { + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { + + if (!m_timeoutHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + m_timeoutHandler->resetTimeOut(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + + if (m_socket->getStatus() & socket::STATUS_WANT_WRITE) { + m_socket->waitForWrite(); + } else { + m_socket->waitForRead(); + } + + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + // Check for transparent characters: '\n..' becomes '\n.' + const char first = receiveBuffer[0]; + + if (first == '.' && last2 == '\n' && last1 == '.') { + + receiveBuffer.erase(receiveBuffer.begin()); + + } else if (receiveBuffer.length() >= 2 && first == '.' && + receiveBuffer[1] == '.' && last1 == '\n') { + + receiveBuffer.erase(receiveBuffer.begin()); + } + + for (size_t trans ; + string::npos != (trans = receiveBuffer.find("\n..")) ; ) { + + receiveBuffer.replace(trans, 3, "\n."); + } + + last1 = receiveBuffer[receiveBuffer.length() - 1]; + last2 = static_cast <char>((receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0); + + // Append the data to the response buffer + buffer += receiveBuffer; + + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(buffer, multiLine); + + // If there is an error (-ERR) when executing a command that + // requires a multi-line response, the error response will + // include only one line, so we stop waiting for a multi-line + // terminator and check for a "normal" one. + if (multiLine && + !foundTerminator && + buffer.length() >= 4 && buffer[0] == '-') { + + foundTerminator = checkTerminator(buffer, false); + } + } +} + + +size_t POP3Response::readResponseImpl( + string& firstLine, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize +) { + + size_t current = 0, total = predictedSize; + + string temp; + bool codeDone = false; + + if (progress) { + progress->start(total); + } + + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + utility::inputStreamSocketAdapter sis(*m_socket); + utility::stopSequenceFilteredInputStream <5> sfis1(sis, "\r\n.\r\n"); + utility::stopSequenceFilteredInputStream <3> sfis2(sfis1, "\n.\n"); + utility::dotFilteredInputStream dfis(sfis2); // "\n.." --> "\n." + + utility::inputStream& is = dfis; + + while (!is.eof()) { + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { + + if (!m_timeoutHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + } + + // Receive data from the socket + byte_t buffer[65536]; + const size_t read = is.read(buffer, sizeof(buffer)); + + if (read == 0) { // buffer is empty + + if (m_socket->getStatus() & socket::STATUS_WANT_WRITE) { + m_socket->waitForWrite(); + } else if (m_socket->getStatus() & socket::STATUS_WANT_READ) { + m_socket->waitForRead(); + } else { + // Input stream needs more bytes to continue, but there + // is enough data into socket buffer. Do not waitForRead(), + // just retry read()ing on the stream. + } + + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + // Notify progress + current += read; + + if (progress) { + total = std::max(total, current); + progress->progress(current, total); + } + + // If we don't have extracted the response code yet + if (!codeDone) { + + vmime::utility::stringUtils::appendBytesToString(temp, buffer, read); + + string responseData; + + if (stripFirstLine(temp, responseData, &firstLine) == true) { + + if (getResponseCode(firstLine) != CODE_OK) { + throw exceptions::command_error("?", firstLine); + } + + codeDone = true; + + os.write(responseData.data(), responseData.length()); + temp.clear(); + + continue; + } + + } else { + + // Inject the data into the output stream + os.write(buffer, read); + } + } + + if (progress) { + progress->stop(total); + } + + return current; +} + + +// static +bool POP3Response::stripFirstLine( + const string& buffer, + string& result, + string* firstLine +) { + + const size_t end = buffer.find('\n'); + + if (end != string::npos) { + + if (firstLine) { + *firstLine = utility::stringUtils::trim(buffer.substr(0, end)); + } + + result = buffer.substr(end + 1); + + return true; + + } else { + + if (firstLine) { + *firstLine = utility::stringUtils::trim(buffer); + } + + result = ""; + + return false; + } +} + + +// static +POP3Response::ResponseCode POP3Response::getResponseCode(const string& buffer) { + + if (buffer.length() >= 2) { + + // +[space] + if (buffer[0] == '+' && + (buffer[1] == ' ' || buffer[1] == '\t')) { + + return CODE_READY; + } + + // +OK + if (buffer.length() >= 3) { + + if (buffer[0] == '+' && + (buffer[1] == 'O' || buffer[1] == 'o') && + (buffer[2] == 'K' || buffer[1] == 'k')) { + + return CODE_OK; + } + } + } + + // -ERR or whatever + return CODE_ERR; +} + + +// static +void POP3Response::stripResponseCode(const string& buffer, string& result) { + + const size_t pos = buffer.find_first_of(" \t"); + + if (pos != string::npos) { + result = buffer.substr(pos + 1); + } else { + result = buffer; + } +} + + +// static +bool POP3Response::checkTerminator(string& buffer, const bool multiLine) { + + // Multi-line response + if (multiLine) { + + static const string term1("\r\n.\r\n"); + static const string term2("\n.\n"); + + return checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2); + + // Normal response + } else { + + static const string term1("\r\n"); + static const string term2("\n"); + + return checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2); + } + + return false; +} + + +// static +bool POP3Response::checkOneTerminator(string& buffer, const string& term) { + + if (buffer.length() >= term.length() && + std::equal(buffer.end() - term.length(), buffer.end(), term.begin())) { + + buffer.erase(buffer.end() - term.length(), buffer.end()); + return true; + } + + return false; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 diff --git a/vmime-master/src/vmime/net/pop3/POP3Response.hpp b/vmime-master/src/vmime/net/pop3/POP3Response.hpp new file mode 100644 index 0000000..69f8d5d --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Response.hpp @@ -0,0 +1,194 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_POP3RESPONSE_HPP_INCLUDED +#define VMIME_NET_SMTP_POP3RESPONSE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" + +#include "vmime/utility/outputStream.hpp" +#include "vmime/utility/progressListener.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/tracer.hpp" + + +namespace vmime { +namespace net { + + +class timeoutHandler; + + +namespace pop3 { + + +class POP3Connection; + + +/** A POP3 response, as sent by the server. + */ +class VMIME_EXPORT POP3Response : public object { + +public: + + /** Possible response codes. */ + enum ResponseCode { + CODE_OK = 0, + CODE_READY, + CODE_ERR + }; + + + /** Receive and parse a POP3 response from the + * specified connection. + * + * @param conn connection from which to read + * @return POP3 response + * @throws exceptions::operation_timed_out if no data + * has been received within the granted time + */ + static shared_ptr <POP3Response> readResponse(const shared_ptr <POP3Connection>& conn); + + /** Receive and parse a multiline POP3 response from + * the specified connection. + * + * @param conn connection from which to read + * @return POP3 response + * @throws exceptions::operation_timed_out if no data + * has been received within the granted time + */ + static shared_ptr <POP3Response> readMultilineResponse(const shared_ptr <POP3Connection>& conn); + + /** Receive and parse a large POP3 response (eg. message data) + * from the specified connection. + * + * @param conn connection from which to read + * @param os output stream to which response data will be written + * @param progress progress listener (can be NULL) + * @param predictedSize estimated size of response data (in bytes) + * @return POP3 response + * @throws exceptions::operation_timed_out if no data + * has been received within the granted time + */ + static shared_ptr <POP3Response> readLargeResponse( + const shared_ptr <POP3Connection>& conn, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize + ); + + + /** Returns whether the response is successful ("OK"). + * + * @return true if the response if successful, false otherwise + */ + bool isSuccess() const; + + /** Return the POP3 response code. + * + * @return response code + */ + ResponseCode getCode() const; + + /** Return the POP3 response text (first line). + * + * @return response text + */ + const string getText() const; + + /** Return the first POP3 response line. + * + * @return first response line + */ + const string getFirstLine() const; + + /** Return the response line at the specified position. + * + * @param pos line index + * @return line at the specified index + */ + const string getLineAt(const size_t pos) const; + + /** Return the number of lines in the response. + * + * @return number of lines in the response + */ + size_t getLineCount() const; + +private: + + POP3Response( + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const shared_ptr <tracer>& tracer + ); + + void readResponseImpl(string& buffer, const bool multiLine); + + size_t readResponseImpl( + string& firstLine, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize + ); + + + static bool stripFirstLine(const string& buffer, string& result, string* firstLine); + + static ResponseCode getResponseCode(const string& buffer); + + static void stripResponseCode(const string& buffer, string& result); + + static bool checkTerminator(string& buffer, const bool multiLine); + static bool checkOneTerminator(string& buffer, const string& term); + + + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + string m_firstLine; + ResponseCode m_code; + string m_text; + + std::vector <string> m_lines; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_SMTP_POP3RESPONSE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3SStore.cpp b/vmime-master/src/vmime/net/pop3/POP3SStore.cpp new file mode 100644 index 0000000..81a50bc --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3SStore.cpp @@ -0,0 +1,82 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3SStore.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3SStore::POP3SStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : POP3Store(sess, auth, true) { + +} + + +POP3SStore::~POP3SStore() { + +} + + +const string POP3SStore::getProtocolName() const { + + return "pop3s"; +} + + + +// Service infos + +POP3ServiceInfos POP3SStore::sm_infos(true); + + +const serviceInfos& POP3SStore::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& POP3SStore::getInfos() const { + + return sm_infos; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3SStore.hpp b/vmime-master/src/vmime/net/pop3/POP3SStore.hpp new file mode 100644 index 0000000..76a6ee1 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3SStore.hpp @@ -0,0 +1,74 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3SSTORE_HPP_INCLUDED +#define VMIME_NET_POP3_POP3SSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/pop3/POP3Store.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** POP3S store service. + */ +class VMIME_EXPORT POP3SStore : public POP3Store { + +public: + + POP3SStore( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ); + + ~POP3SStore(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static POP3ServiceInfos sm_infos; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3SSTORE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.cpp b/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.cpp new file mode 100644 index 0000000..4deee74 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.cpp @@ -0,0 +1,142 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3ServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3ServiceInfos::POP3ServiceInfos(const bool pop3s) + : m_pop3s(pop3s) { + +} + + +const string POP3ServiceInfos::getPropertyPrefix() const { + + if (m_pop3s) { + return "store.pop3s."; + } else { + return "store.pop3."; + } +} + + +const POP3ServiceInfos::props& POP3ServiceInfos::getProperties() const { + + static props pop3Props = { + // POP3-specific options + property("options.apop", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.apop.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "110"), + }; + + static props pop3sProps = { + + // POP3-specific options + property("options.apop", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.apop.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "995"), + }; + + return m_pop3s ? pop3sProps : pop3Props; +} + + +const std::vector <serviceInfos::property> POP3ServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + // POP3-specific options + list.push_back(p.PROPERTY_OPTIONS_APOP); + list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK); +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_pop3s) { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + + return list; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.hpp b/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.hpp new file mode 100644 index 0000000..590a6be --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3ServiceInfos.hpp @@ -0,0 +1,91 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3SERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_POP3_POP3SERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** Information about POP3 service. + */ +class VMIME_EXPORT POP3ServiceInfos : public serviceInfos { + +public: + + POP3ServiceInfos(const bool pop3s); + + struct props { + // POP3-specific options + serviceInfos::property PROPERTY_OPTIONS_APOP; + serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK; +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; + +private: + + const bool m_pop3s; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3SERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/pop3/POP3Store.cpp b/vmime-master/src/vmime/net/pop3/POP3Store.cpp new file mode 100644 index 0000000..b06640f --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Store.cpp @@ -0,0 +1,262 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Store.hpp" +#include "vmime/net/pop3/POP3Folder.hpp" +#include "vmime/net/pop3/POP3Command.hpp" +#include "vmime/net/pop3/POP3Response.hpp" + +#include "vmime/exception.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Store::POP3Store( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured +) + : store(sess, getInfosInstance(), auth), + m_isPOP3S(secured) { + +} + + +POP3Store::~POP3Store() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string POP3Store::getProtocolName() const { + + return "pop3"; +} + + +shared_ptr <folder> POP3Store::getDefaultFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <POP3Folder>( + new POP3Folder( + folder::path(folder::path::component("INBOX")), + dynamicCast <POP3Store>(shared_from_this()) + ) + ); +} + + +shared_ptr <folder> POP3Store::getRootFolder() { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <POP3Folder>( + new POP3Folder( + folder::path(), + dynamicCast <POP3Store>(shared_from_this()) + ) + ); +} + + +shared_ptr <folder> POP3Store::getFolder(const folder::path& path) { + + if (!isConnected()) { + throw exceptions::illegal_state("Not connected"); + } + + return shared_ptr <POP3Folder>( + new POP3Folder( + path, + dynamicCast <POP3Store>(shared_from_this()) + ) + ); +} + + +bool POP3Store::isValidFolderName(const folder::path::component& /* name */) const { + + return true; +} + + +void POP3Store::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + m_connection = make_shared <POP3Connection>( + dynamicCast <POP3Store>(shared_from_this()), getAuthenticator() + ); + + m_connection->connect(); +} + + +bool POP3Store::isPOP3S() const { + + return m_isPOP3S; +} + + +bool POP3Store::isConnected() const { + + return m_connection && m_connection->isConnected(); +} + + +bool POP3Store::isSecuredConnection() const { + + if (!m_connection) { + return false; + } + + return m_connection->isSecuredConnection(); +} + + +shared_ptr <connectionInfos> POP3Store::getConnectionInfos() const { + + if (!m_connection) { + return null; + } + + return m_connection->getConnectionInfos(); +} + + +shared_ptr <POP3Connection> POP3Store::getConnection() { + + return m_connection; +} + + +void POP3Store::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + for (std::list <POP3Folder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) { + + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + m_connection->disconnect(); + m_connection = null; +} + + +void POP3Store::noop() { + + if (!m_connection) { + throw exceptions::not_connected(); + } + + POP3Command::NOOP()->send(m_connection); + + shared_ptr <POP3Response> response = POP3Response::readResponse(m_connection); + + if (!response->isSuccess()) { + throw exceptions::command_error("NOOP", response->getFirstLine()); + } +} + + +void POP3Store::registerFolder(POP3Folder* folder) { + + m_folders.push_back(folder); +} + + +void POP3Store::unregisterFolder(POP3Folder* folder) { + + std::list <POP3Folder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + + if (it != m_folders.end()) { + m_folders.erase(it); + } +} + + +int POP3Store::getCapabilities() const { + + return CAPABILITY_DELETE_MESSAGE; +} + + + +// Service infos + +POP3ServiceInfos POP3Store::sm_infos(false); + + +const serviceInfos& POP3Store::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& POP3Store::getInfos() const { + + return sm_infos; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3Store.hpp b/vmime-master/src/vmime/net/pop3/POP3Store.hpp new file mode 100644 index 0000000..140a1ab --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Store.hpp @@ -0,0 +1,120 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3STORE_HPP_INCLUDED +#define VMIME_NET_POP3_POP3STORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include "vmime/net/store.hpp" + +#include "vmime/net/pop3/POP3ServiceInfos.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" + +#include "vmime/utility/stream.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +class POP3Folder; +class POP3Command; +class POP3Response; + + +/** POP3 store service. + */ +class VMIME_EXPORT POP3Store : public store { + + friend class POP3Folder; + friend class POP3Message; + +public: + + POP3Store( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured = false + ); + + ~POP3Store(); + + const string getProtocolName() const; + + shared_ptr <folder> getDefaultFolder(); + shared_ptr <folder> getRootFolder(); + shared_ptr <folder> getFolder(const folder::path& path); + + bool isValidFolderName(const folder::path::component& name) const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + int getCapabilities() const; + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + shared_ptr <POP3Connection> getConnection(); + + bool isPOP3S() const; + +private: + + shared_ptr <POP3Connection> m_connection; + + + void registerFolder(POP3Folder* folder); + void unregisterFolder(POP3Folder* folder); + + std::list <POP3Folder*> m_folders; + + + const bool m_isPOP3S; + + + // Service infos + static POP3ServiceInfos sm_infos; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3STORE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/pop3/POP3Utils.cpp b/vmime-master/src/vmime/net/pop3/POP3Utils.cpp new file mode 100644 index 0000000..b38161e --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Utils.cpp @@ -0,0 +1,135 @@ +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Utils.hpp" +#include "vmime/net/pop3/POP3Response.hpp" + +#include <sstream> + + +namespace vmime { +namespace net { +namespace pop3 { + + +// static +void POP3Utils::parseMultiListOrUidlResponse( + const shared_ptr <POP3Response>& response, + std::map <size_t, string>& result +) { + + std::map <size_t, string> ids; + + for (size_t i = 0, n = response->getLineCount() ; i < n ; ++i) { + + string line = response->getLineAt(i); + string::iterator it = line.begin(); + + while (it != line.end() && (*it == ' ' || *it == '\t')) { + ++it; + } + + if (it != line.end()) { + + size_t number = 0; + + while (it != line.end() && (*it >= '0' && *it <= '9')) { + number = (number * 10) + (*it - '0'); + ++it; + } + + while (it != line.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != line.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != line.end()) { + result.insert(std::map <size_t, string>::value_type(number, string(it, line.end()))); + } + } + } +} + + + +class POP3MessageSetEnumerator : public messageSetEnumerator { + +public: + + POP3MessageSetEnumerator(const size_t msgCount) + : m_msgCount(msgCount) { + + } + + void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range) { + + size_t last = range.getLast(); + + if (last == size_t(-1)) { + last = m_msgCount; + } + + for (size_t i = range.getFirst() ; i <= last ; ++i) { + list.push_back(i); + } + } + + void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */) { + + // Not supported + } + +public: + + std::vector <size_t> list; + +private: + + size_t m_msgCount; +}; + + +// static +const std::vector <size_t> POP3Utils::messageSetToNumberList( + const messageSet& msgs, + const size_t msgCount +) { + + POP3MessageSetEnumerator en(msgCount); + msgs.enumerate(en); + + return en.list; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + diff --git a/vmime-master/src/vmime/net/pop3/POP3Utils.hpp b/vmime-master/src/vmime/net/pop3/POP3Utils.hpp new file mode 100644 index 0000000..09d15d5 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Utils.hpp @@ -0,0 +1,92 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3UTILS_HPP_INCLUDED +#define VMIME_NET_POP3_POP3UTILS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + + +#include <map> + +#include "vmime/types.hpp" + +#include "vmime/net/messageSet.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +class POP3Response; + + +class VMIME_EXPORT POP3Utils { + +public: + + /** Parse a response of type ([integer] [string] \n)*. + * This is used in LIST or UIDL commands: + * + * C: UIDL + * S: +OK + * S: 1 whqtswO00WBw418f9t5JxYwZ + * S: 2 QhdPYR:00WBw1Ph7x7 + * S: . + * + * @param response raw response string as returned by the server + * @param result points to an associative array which maps a message + * number to its corresponding data (either UID or size) + */ + static void parseMultiListOrUidlResponse( + const shared_ptr <POP3Response>& response, + std::map <size_t, string>& result + ); + + /** Returns a list of message numbers given a message set. + * + * @param msgs message set + * @param msgCount number of messages in folder + * @return list of message numbers + */ + static const std::vector <size_t> messageSetToNumberList( + const messageSet& msgs, + const size_t msgCount + ); +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 + +#endif // VMIME_NET_POP3_POP3UTILS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/pop3/pop3.hpp b/vmime-master/src/vmime/net/pop3/pop3.hpp new file mode 100644 index 0000000..ced3a97 --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/pop3.hpp @@ -0,0 +1,35 @@ +// +// 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. +// + +#ifndef VMIME_NET_POP3_POP3_HPP_INCLUDED +#define VMIME_NET_POP3_POP3_HPP_INCLUDED + + +#include "vmime/net/pop3/POP3Folder.hpp" +#include "vmime/net/pop3/POP3FolderStatus.hpp" +#include "vmime/net/pop3/POP3Message.hpp" +#include "vmime/net/pop3/POP3Store.hpp" +#include "vmime/net/pop3/POP3SStore.hpp" + + +#endif // VMIME_NET_POP3_POP3_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/securedConnectionInfos.hpp b/vmime-master/src/vmime/net/securedConnectionInfos.hpp new file mode 100644 index 0000000..6025801 --- /dev/null +++ b/vmime-master/src/vmime/net/securedConnectionInfos.hpp @@ -0,0 +1,55 @@ +// +// 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. +// + +#ifndef VMIME_NET_SECUREDCONNECTIONINFOS_HPP_INCLUDED +#define VMIME_NET_SECUREDCONNECTIONINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/connectionInfos.hpp" + + +namespace vmime { +namespace net { + + +/** Information about the secured connection used by a service. + */ +class VMIME_EXPORT securedConnectionInfos : public connectionInfos { + +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SECUREDCONNECTIONINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/sendmail/sendmail.hpp b/vmime-master/src/vmime/net/sendmail/sendmail.hpp new file mode 100644 index 0000000..a8d1412 --- /dev/null +++ b/vmime-master/src/vmime/net/sendmail/sendmail.hpp @@ -0,0 +1,31 @@ +// +// 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. +// + +#ifndef VMIME_NET_SENDMAIL_SENDMAIL_HPP_INCLUDED +#define VMIME_NET_SENDMAIL_SENDMAIL_HPP_INCLUDED + + +#include "vmime/net/sendmail/sendmailTransport.hpp" + + +#endif // VMIME_NET_SENDMAIL_SENDMAIL_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.cpp b/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.cpp new file mode 100644 index 0000000..b865446 --- /dev/null +++ b/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.cpp @@ -0,0 +1,77 @@ +// +// 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_SENDMAIL + + +#include "vmime/net/sendmail/sendmailServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace sendmail { + + +sendmailServiceInfos::sendmailServiceInfos() { + +} + + +const string sendmailServiceInfos::getPropertyPrefix() const { + + return "transport.sendmail."; +} + + +const sendmailServiceInfos::props& sendmailServiceInfos::getProperties() const { + + static props sendmailProps = { + // Path to sendmail (override default) + property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH)) + }; + + return sendmailProps; +} + + +const std::vector <serviceInfos::property> sendmailServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + list.push_back(p.PROPERTY_BINPATH); + + return list; +} + + +} // sendmail +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + diff --git a/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.hpp b/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.hpp new file mode 100644 index 0000000..bfec2e0 --- /dev/null +++ b/vmime-master/src/vmime/net/sendmail/sendmailServiceInfos.hpp @@ -0,0 +1,69 @@ +// +// 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. +// + +#ifndef VMIME_NET_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace sendmail { + + +/** Information about sendmail service. + */ +class VMIME_EXPORT sendmailServiceInfos : public serviceInfos { + +public: + + sendmailServiceInfos(); + + struct props { + serviceInfos::property PROPERTY_BINPATH; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; +}; + + +} // sendmail +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + +#endif // VMIME_NET_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/sendmail/sendmailTransport.cpp b/vmime-master/src/vmime/net/sendmail/sendmailTransport.cpp new file mode 100644 index 0000000..7010fd8 --- /dev/null +++ b/vmime-master/src/vmime/net/sendmail/sendmailTransport.cpp @@ -0,0 +1,244 @@ +// +// 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_SENDMAIL + + +#include "vmime/net/sendmail/sendmailTransport.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" +#include "vmime/message.hpp" +#include "vmime/mailboxList.hpp" + +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/childProcess.hpp" + +#include "vmime/utility/streamUtils.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + +#include "vmime/config.hpp" + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const sendmailServiceInfos&>(getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (getInfos().hasProperty(getSession(), \ + dynamic_cast <const sendmailServiceInfos&>(getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace sendmail { + + +sendmailTransport::sendmailTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : transport(sess, getInfosInstance(), auth), + m_connected(false) { + +} + + +sendmailTransport::~sendmailTransport() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string sendmailTransport::getProtocolName() const { + + return "sendmail"; +} + + +void sendmailTransport::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + // Use the specified path for 'sendmail' or a default one if no path is specified + m_sendmailPath = GET_PROPERTY(string, PROPERTY_BINPATH); + + m_connected = true; +} + + +bool sendmailTransport::isConnected() const { + + return m_connected; +} + + +bool sendmailTransport::isSecuredConnection() const { + + return false; +} + + +shared_ptr <connectionInfos> sendmailTransport::getConnectionInfos() const { + + return make_shared <defaultConnectionInfos>("localhost", static_cast <port_t>(0)); +} + + +void sendmailTransport::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + internalDisconnect(); +} + + +void sendmailTransport::internalDisconnect() { + + m_connected = false; +} + + +void sendmailTransport::noop() { + + // Do nothing +} + + +void sendmailTransport::send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& /*dsnAttrs*/ +) { + + // If no recipient/expeditor was found, throw an exception + if (recipients.isEmpty()) { + throw exceptions::no_recipient(); + } else if (expeditor.isEmpty()) { + throw exceptions::no_expeditor(); + } + + // Construct the argument list + std::vector <string> args; + + args.push_back("-i"); + args.push_back("-f"); + + if (!sender.isEmpty()) { + args.push_back(expeditor.getEmail().generate()); + } else { + args.push_back(sender.getEmail().generate()); + } + + args.push_back("--"); + + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + args.push_back(recipients.getMailboxAt(i)->getEmail().generate()); + } + + // Call sendmail + try { + internalSend(args, is, size, progress); + } catch (vmime::exception& e) { + throw exceptions::command_error("SEND", "", "sendmail failed", e); + } +} + + +void sendmailTransport::internalSend( + const std::vector <string>& args, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress +) { + + const utility::file::path path = vmime::platform::getHandler()-> + getFileSystemFactory()->stringToPath(m_sendmailPath); + + shared_ptr <utility::childProcess> proc = + vmime::platform::getHandler()->getChildProcessFactory()->create(path); + + proc->start(args, utility::childProcess::FLAG_REDIRECT_STDIN); + + // Copy message data from input stream to output pipe + utility::outputStream& os = *(proc->getStdIn()); + + // Workaround for lame sendmail implementations that + // can't handle CRLF eoln sequences: we transform CRLF + // sequences into LF characters. + utility::CRLFToLFFilteredOutputStream fos(os); + + // TODO: remove 'Bcc:' field from message header + + utility::bufferedStreamCopy(is, fos, size, progress); + + // Wait for sendmail to exit + proc->waitForFinish(); +} + + +// Service infos + +sendmailServiceInfos sendmailTransport::sm_infos; + + +const serviceInfos& sendmailTransport::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& sendmailTransport::getInfos() const { + + return sm_infos; +} + + +} // sendmail +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + diff --git a/vmime-master/src/vmime/net/sendmail/sendmailTransport.hpp b/vmime-master/src/vmime/net/sendmail/sendmailTransport.hpp new file mode 100644 index 0000000..ce2cfe9 --- /dev/null +++ b/vmime-master/src/vmime/net/sendmail/sendmailTransport.hpp @@ -0,0 +1,112 @@ +// +// 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. +// + +#ifndef VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED +#define VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + + +#include "vmime/net/transport.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/net/sendmail/sendmailServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace sendmail { + + +/** Sendmail local transport service. + */ +class VMIME_EXPORT sendmailTransport : public transport { + +public: + + sendmailTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ); + + ~sendmailTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + void send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + +private: + + void internalDisconnect(); + + void internalSend( + const std::vector <string>& args, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress + ); + + + string m_sendmailPath; + + bool m_connected; + + + // Service infos + static sendmailServiceInfos sm_infos; +}; + + +} // sendmail +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SENDMAIL + +#endif // VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/service.cpp b/vmime-master/src/vmime/net/service.cpp new file mode 100644 index 0000000..b43c3e2 --- /dev/null +++ b/vmime-master/src/vmime/net/service.cpp @@ -0,0 +1,170 @@ +// +// 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 + + +#include "vmime/net/service.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/net/defaultTimeoutHandler.hpp" + +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/defaultSASLAuthenticator.hpp" +#else + #include "vmime/security/defaultAuthenticator.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/security/cert/defaultCertificateVerifier.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + + +namespace vmime { +namespace net { + + +service::service( + const shared_ptr <session>& sess, + const serviceInfos& /* infos */, + const shared_ptr <security::authenticator>& auth +) + : m_session(sess), + m_auth(auth) { + + if (!auth) { + +#if VMIME_HAVE_SASL_SUPPORT + m_auth = make_shared <security::sasl::defaultSASLAuthenticator>(); +#else + m_auth = make_shared <security::defaultAuthenticator>(); +#endif // VMIME_HAVE_SASL_SUPPORT + } + +#if VMIME_HAVE_TLS_SUPPORT + m_certVerifier = make_shared <security::cert::defaultCertificateVerifier>(); +#endif // VMIME_HAVE_TLS_SUPPORT + + m_socketFactory = platform::getHandler()->getSocketFactory(); + + m_toHandlerFactory = make_shared <defaultTimeoutHandlerFactory>(); +} + + +service::~service() { + +} + + +shared_ptr <const session> service::getSession() const { + + return m_session; +} + + +shared_ptr <session> service::getSession() { + + return m_session; +} + + +shared_ptr <const security::authenticator> service::getAuthenticator() const { + + return m_auth; +} + + +shared_ptr <security::authenticator> service::getAuthenticator() { + + return m_auth; +} + + +void service::setAuthenticator(const shared_ptr <security::authenticator>& auth) { + + m_auth = auth; +} + + +#if VMIME_HAVE_TLS_SUPPORT + +void service::setCertificateVerifier(const shared_ptr <security::cert::certificateVerifier>& cv) { + + m_certVerifier = cv; +} + + +shared_ptr <security::cert::certificateVerifier> service::getCertificateVerifier() { + + return m_certVerifier; +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +void service::setSocketFactory(const shared_ptr <socketFactory>& sf) { + + m_socketFactory = sf; +} + + +shared_ptr <socketFactory> service::getSocketFactory() { + + return m_socketFactory; +} + + +void service::setTracerFactory(const shared_ptr <tracerFactory>& tf) { + + m_tracerFactory = tf; +} + + +shared_ptr <tracerFactory> service::getTracerFactory() { + + return m_tracerFactory; +} + + +void service::setTimeoutHandlerFactory(const shared_ptr <timeoutHandlerFactory>& thf) { + + m_toHandlerFactory = thf; +} + + +shared_ptr <timeoutHandlerFactory> service::getTimeoutHandlerFactory() { + + return m_toHandlerFactory; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/service.hpp b/vmime-master/src/vmime/net/service.hpp new file mode 100644 index 0000000..a1869de --- /dev/null +++ b/vmime-master/src/vmime/net/service.hpp @@ -0,0 +1,239 @@ +// +// 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. +// + +#ifndef VMIME_NET_SERVICE_HPP_INCLUDED +#define VMIME_NET_SERVICE_HPP_INCLUDED + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/types.hpp" + +#include "vmime/net/session.hpp" + +#include "vmime/net/serviceInfos.hpp" +#include "vmime/net/connectionInfos.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/tracer.hpp" + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/security/cert/certificateVerifier.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace net { + + +/** Base class for messaging services. + */ +class VMIME_EXPORT service : public object, public enable_shared_from_this <service> { + +protected: + + service( + const shared_ptr <session>& sess, + const serviceInfos& infos, + const shared_ptr <security::authenticator>& auth + ); + +public: + + virtual ~service(); + + /** Possible service types. */ + enum Type { + TYPE_STORE = 0, /**< The service is a message store. */ + TYPE_TRANSPORT /**< The service sends messages. */ + }; + + /** Return the type of service. + * + * @return type of service + */ + virtual Type getType() const = 0; + + /** Return the protocol name of this service. + * + * @return protocol name + */ + virtual const string getProtocolName() const = 0; + + /** Return the session object associated with this service instance. + * + * @return session object + */ + shared_ptr <const session> getSession() const; + + /** Return the session object associated with this service instance. + * + * @return session object + */ + shared_ptr <session> getSession(); + + /** Return information about this service. + * + * @return information about the service + */ + virtual const serviceInfos& getInfos() const = 0; + + /** Connect to service. + */ + virtual void connect() = 0; + + /** Disconnect from service. + */ + virtual void disconnect() = 0; + + /** Test whether this service is connected. + * + * @return true if the service is connected, false otherwise + */ + virtual bool isConnected() const = 0; + + /** Do nothing but ensure the server do not disconnect (for + * example, this can reset the auto-logout timer on the + * server, if one exists). + */ + virtual void noop() = 0; + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + shared_ptr <const security::authenticator> getAuthenticator() const; + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + shared_ptr <security::authenticator> getAuthenticator(); + + /** Set the authenticator object used with this service instance. + * + * @param auth authenticator object + */ + void setAuthenticator(const shared_ptr <security::authenticator>& auth); + +#if VMIME_HAVE_TLS_SUPPORT + + /** Set the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + void setCertificateVerifier(const shared_ptr <security::cert::certificateVerifier>& cv); + + /** Get the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + shared_ptr <security::cert::certificateVerifier> getCertificateVerifier(); + +#endif // VMIME_HAVE_TLS_SUPPORT + + /** Set the factory used to create socket objects for this + * service. + * + * @param sf socket factory + */ + void setSocketFactory(const shared_ptr <socketFactory>& sf); + + /** Return the factory used to create socket objects for this + * service. + * + * @return socket factory + */ + shared_ptr <socketFactory> getSocketFactory(); + + /** Set the factory used to create timeoutHandler objects for + * this service. By default, the defaultTimeoutHandler class + * is used. Not all services support timeout handling. + * + * @param thf timeoutHandler factory + */ + void setTimeoutHandlerFactory(const shared_ptr <timeoutHandlerFactory>& thf); + + /** Return the factory used to create timeoutHandler objects for + * this service. + * + * @return timeoutHandler factory + */ + shared_ptr <timeoutHandlerFactory> getTimeoutHandlerFactory(); + + + void setTracerFactory(const shared_ptr <tracerFactory>& tf); + + shared_ptr <tracerFactory> getTracerFactory(); + + /** Set a property for this service (service prefix is added automatically). + * + * WARNING: this sets the property on the session object, so all service + * instances created with the session object will inherit the property. + * + * @param name property name + * @param value property value + */ + template <typename TYPE> + void setProperty(const string& name, const TYPE& value) { + m_session->getProperties()[getInfos().getPropertyPrefix() + name] = value; + } + + /** Check whether the connection is secured. + * + * @return true if the connection is secured, false otherwise + */ + virtual bool isSecuredConnection() const = 0; + + /** Get information about the connection. + * + * @return information about the connection + */ + virtual shared_ptr <connectionInfos> getConnectionInfos() const = 0; + +private: + + shared_ptr <session> m_session; + shared_ptr <security::authenticator> m_auth; + +#if VMIME_HAVE_TLS_SUPPORT + shared_ptr <security::cert::certificateVerifier> m_certVerifier; +#endif // VMIME_HAVE_TLS_SUPPORT + + shared_ptr <socketFactory> m_socketFactory; + shared_ptr <timeoutHandlerFactory> m_toHandlerFactory; + shared_ptr <tracerFactory> m_tracerFactory; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SERVICE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/serviceFactory.cpp b/vmime-master/src/vmime/net/serviceFactory.cpp new file mode 100644 index 0000000..72b96fe --- /dev/null +++ b/vmime-master/src/vmime/net/serviceFactory.cpp @@ -0,0 +1,157 @@ +// +// 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 + + +#include "vmime/net/serviceFactory.hpp" +#include "vmime/net/service.hpp" + +#include "vmime/exception.hpp" + +#include "vmime/net/builtinServices.inl" + + +namespace vmime { +namespace net { + + +serviceFactory::serviceFactory() { + +} + + +serviceFactory::~serviceFactory() { + +} + + +shared_ptr <serviceFactory> serviceFactory::getInstance() { + + static serviceFactory instance; + return shared_ptr <serviceFactory>(&instance, noop_shared_ptr_deleter <serviceFactory>()); +} + + +shared_ptr <service> serviceFactory::create( + const shared_ptr <session>& sess, + const string& protocol, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <const registeredService> rserv = getServiceByProtocol(protocol); + + if (!rserv) { + throw exceptions::no_factory_available("No service is registered for protocol '" + protocol + "'."); + } + + return rserv->create(sess, auth); +} + + +shared_ptr <service> serviceFactory::create( + const shared_ptr <session>& sess, + const utility::url& u, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <service> serv = create(sess, u.getProtocol(), auth); + + sess->getProperties()[serv->getInfos().getPropertyPrefix() + "server.address"] = u.getHost(); + + if (u.getPort() != utility::url::UNSPECIFIED_PORT) { + sess->getProperties()[serv->getInfos().getPropertyPrefix() + "server.port"] = u.getPort(); + } + + // Path portion of the URL is used to point a specific folder (empty = root). + // In maildir, this is used to point to the root of the message repository. + if (!u.getPath().empty()) { + sess->getProperties()[serv->getInfos().getPropertyPrefix() + "server.rootpath"] = u.getPath(); + } + + if (!u.getUsername().empty()) { + sess->getProperties()[serv->getInfos().getPropertyPrefix() + "auth.username"] = u.getUsername(); + sess->getProperties()[serv->getInfos().getPropertyPrefix() + "auth.password"] = u.getPassword(); + } + + return serv; +} + + +shared_ptr <const serviceFactory::registeredService> serviceFactory::getServiceByProtocol(const string& protocol) const { + + const string name(utility::stringUtils::toLower(protocol)); + + for (std::vector <shared_ptr <registeredService> >::const_iterator it = m_services.begin() ; + it != m_services.end() ; ++it) { + + if ((*it)->getName() == name) { + return (*it); + } + } + + return null; +} + + +size_t serviceFactory::getServiceCount() const { + + return m_services.size(); +} + + +shared_ptr <const serviceFactory::registeredService> serviceFactory::getServiceAt(const size_t pos) const { + + return m_services[pos]; +} + + +const std::vector <shared_ptr <const serviceFactory::registeredService> > serviceFactory::getServiceList() const { + + std::vector <shared_ptr <const registeredService> > res; + + for (std::vector <shared_ptr <registeredService> >::const_iterator it = m_services.begin() ; + it != m_services.end() ; ++it) { + + res.push_back(*it); + } + + return (res); +} + + +void serviceFactory::registerService(const shared_ptr <registeredService>& reg) { + + m_services.push_back(reg); +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/serviceFactory.hpp b/vmime-master/src/vmime/net/serviceFactory.hpp new file mode 100644 index 0000000..8911e08 --- /dev/null +++ b/vmime-master/src/vmime/net/serviceFactory.hpp @@ -0,0 +1,168 @@ +// +// 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. +// + +#ifndef VMIME_NET_SERVICEFACTORY_HPP_INCLUDED +#define VMIME_NET_SERVICEFACTORY_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <map> + +#include "vmime/types.hpp" +#include "vmime/base.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/url.hpp" + +#include "vmime/net/service.hpp" +#include "vmime/net/serviceInfos.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/security/authenticator.hpp" + +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace net { + + +class session; + + +/** A factory to create 'service' objects for a specified protocol. + */ + +class VMIME_EXPORT serviceFactory { + +private: + + serviceFactory(); + ~serviceFactory(); + +public: + + static shared_ptr <serviceFactory> getInstance(); + + /** Information about a registered service. */ + class registeredService : public object { + + friend class serviceFactory; + + protected: + + virtual ~registeredService() { } + + public: + + virtual shared_ptr <service> create( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ) const = 0; + + virtual int getType() const = 0; + virtual const string& getName() const = 0; + virtual const serviceInfos& getInfos() const = 0; + }; + + + /** Register a new service by its protocol name. + * + * @param reg service registration infos + */ + void registerService(const shared_ptr <registeredService>& reg); + + /** Create a new service instance from a protocol name. + * + * @param sess session + * @param protocol protocol name (eg. "pop3") + * @param auth authenticator used to provide credentials (can be NULL if not used) + * @return a new service instance for the specified protocol, or NULL if no service + * is registered for this protocol + */ + shared_ptr <service> create( + const shared_ptr <session>& sess, + const string& protocol, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Create a new service instance from a URL. + * + * @param sess session + * @param u full URL with at least protocol and server (you can also specify + * port, username and password) + * @param auth authenticator used to provide credentials (can be NULL if not used) + * @return a new service instance for the specified protocol or NULL if no service + * is registered for this protocol + */ + shared_ptr <service> create( + const shared_ptr <session>& sess, + const utility::url& u, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return information about a registered protocol. + * + * @param protocol protocol name + * @return information about this protocol, or NULL if no service is registered + * for this protocol + */ + shared_ptr <const registeredService> getServiceByProtocol(const string& protocol) const; + + /** Return the number of registered services. + * + * @return number of registered services + */ + size_t getServiceCount() const; + + /** Return the registered service at the specified position. + * + * @param pos position of the registered service to return + * @return registered service at the specified position + */ + shared_ptr <const registeredService> getServiceAt(const size_t pos) const; + + /** Return a list of all registered services. + * + * @return list of registered services + */ + const std::vector <shared_ptr <const registeredService> > getServiceList() const; + +private: + + std::vector <shared_ptr <registeredService> > m_services; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SERVICEFACTORY_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/serviceInfos.cpp b/vmime-master/src/vmime/net/serviceInfos.cpp new file mode 100644 index 0000000..2c7b044 --- /dev/null +++ b/vmime-master/src/vmime/net/serviceInfos.cpp @@ -0,0 +1,198 @@ +// +// 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 + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { + + +// Common properties +const serviceInfos::property serviceInfos::property::SERVER_ADDRESS( + "server.address", + serviceInfos::property::TYPE_STRING +); + +const serviceInfos::property serviceInfos::property::SERVER_PORT( + "server.port", + serviceInfos::property::TYPE_INTEGER +); + +const serviceInfos::property serviceInfos::property::SERVER_ROOTPATH( + "server.rootpath", + serviceInfos::property::TYPE_STRING +); + +const serviceInfos::property serviceInfos::property::AUTH_USERNAME( + "auth.username", + serviceInfos::property::TYPE_STRING +); + +const serviceInfos::property serviceInfos::property::AUTH_PASSWORD( + "auth.password", + serviceInfos::property::TYPE_STRING +); + +const serviceInfos::property serviceInfos::property::AUTH_ACCESS_TOKEN( + "auth.accesstoken", + serviceInfos::property::TYPE_STRING +); + +#if VMIME_HAVE_TLS_SUPPORT + +const serviceInfos::property serviceInfos::property::CONNECTION_TLS( + "connection.tls", + serviceInfos::property::TYPE_BOOLEAN, + "false" +); + +const serviceInfos::property serviceInfos::property::CONNECTION_TLS_REQUIRED( + "connection.tls.required", + serviceInfos::property::TYPE_BOOLEAN, + "false" +); + +#endif // VMIME_HAVE_TLS_SUPPORT + + + +// serviceInfos + +serviceInfos::serviceInfos() { + +} + + +serviceInfos::serviceInfos(const serviceInfos&) { + +} + + +serviceInfos& serviceInfos::operator=(const serviceInfos&) { + + return *this; +} + + +serviceInfos::~serviceInfos() { + +} + + +bool serviceInfos::hasProperty(const shared_ptr <session>& s, const property& p) const { + return s->getProperties().hasProperty(getPropertyPrefix() + p.getName()); +} + + + +// serviceInfos::property + +serviceInfos::property::property( + const string& name, + const Types type, + const string& defaultValue, + const int flags +) + : m_name(name), + m_defaultValue(defaultValue), + m_type(type), + m_flags(flags) { + +} + + +serviceInfos::property::property( + const property& p, + const int addFlags, + const int removeFlags +) { + + m_name = p.m_name; + m_type = p.m_type; + m_defaultValue = p.m_defaultValue; + m_flags = (p.m_flags | addFlags) & ~removeFlags; +} + + +serviceInfos::property::property( + const property& p, + const string& newDefaultValue, + const int addFlags, + const int removeFlags +) { + + m_name = p.m_name; + m_type = p.m_type; + m_defaultValue = newDefaultValue; + m_flags = (p.m_flags | addFlags) & ~removeFlags; +} + + +serviceInfos::property& serviceInfos::property::operator=(const property& p) { + + m_name = p.m_name; + m_type = p.m_type; + m_defaultValue = p.m_defaultValue; + m_flags = p.m_flags; + + return *this; +} + + +const string& serviceInfos::property::getName() const { + + return m_name; +} + + +const string& serviceInfos::property::getDefaultValue() const { + + return m_defaultValue; +} + + +serviceInfos::property::Types serviceInfos::property::getType() const { + + return m_type; +} + + +int serviceInfos::property::getFlags() const { + + return m_flags; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/serviceInfos.hpp b/vmime-master/src/vmime/net/serviceInfos.hpp new file mode 100644 index 0000000..1c4ac99 --- /dev/null +++ b/vmime-master/src/vmime/net/serviceInfos.hpp @@ -0,0 +1,263 @@ +// +// 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. +// + +#ifndef VMIME_NET_SERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SERVICEINFOS_HPP_INCLUDED + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include <vector> + +#include "vmime/types.hpp" + +#include "vmime/net/session.hpp" + + +namespace vmime { +namespace net { + + +/** Stores information about a messaging service. + */ +class VMIME_EXPORT serviceInfos { + + friend class serviceFactory; + +protected: + + serviceInfos(); + serviceInfos(const serviceInfos&); + +private: + + serviceInfos& operator=(const serviceInfos&); + +public: + + virtual ~serviceInfos(); + + + /** A service property. + */ + class VMIME_EXPORT property { + + public: + + /** The common property 'server.address' which is + * the host name or the IP address of the server. */ + static const property SERVER_ADDRESS; + + /** The common property 'server.port' which is + * the port used to connect to the server. */ + static const property SERVER_PORT; + + /** The common property 'server.rootpath' which is + * the full path of the folder on the server (for + * maildir, this is the local filesystem directory). */ + static const property SERVER_ROOTPATH; + + /** The common property 'auth.username' which is the + * username used to authenticate with the server. */ + static const property AUTH_USERNAME; + + /** The common property 'auth.password' which is the + * password used to authenticate with the server. */ + static const property AUTH_PASSWORD; + + /** The common property 'auth.accesstoken' which is the + * access token used to authenticate with the server. */ + static const property AUTH_ACCESS_TOKEN; + +#if VMIME_HAVE_TLS_SUPPORT + + /** The common property 'connection.tls': this is used to + * start a secured connection if it is supported by the + * server (STARTTLS extension). + */ + static const property CONNECTION_TLS; + + /** The common property 'connection.tls.required' should be + * set to 'true' to make the connection process fail if the + * server can't start a secured connection (no effect if + * 'connection.tls' is not set to 'true'). + */ + static const property CONNECTION_TLS_REQUIRED; + +#endif // VMIME_HAVE_TLS_SUPPORT + + + /** Value types. + */ + enum Types { + TYPE_INTEGER, /*< Integer number. */ + TYPE_STRING, /*< Character string. */ + TYPE_BOOLEAN, /*< Boolean (true or false). */ + + TYPE_DEFAULT = TYPE_STRING + }; + + /** Property flags. + */ + enum Flags { + FLAG_NONE = 0, /*< No flags. */ + FLAG_REQUIRED = (1 << 0), /*< The property must be valued. */ + FLAG_HIDDEN = (1 << 1), /*< The property should not be shown + to the user but can be modified. */ + + FLAG_DEFAULT = FLAG_NONE /*< Default flags. */ + }; + + + /** Construct a new property. + * + * @param name property name + * @param type value type + * @param defaultValue default value + * @param flags property attributes + */ + property( + const string& name, + const Types type, + const string& defaultValue = "", + const int flags = FLAG_DEFAULT + ); + + /** Construct a new property from an existing property. + * + * @param p source property + * @param addFlags flags to add + * @param removeFlags flags to remove + */ + property( + const property& p, + const int addFlags = FLAG_NONE, + const int removeFlags = FLAG_NONE + ); + + /** Construct a new property from an existing property. + * + * @param p source property + * @param newDefaultValue new default value + * @param addFlags flags to add + * @param removeFlags flags to remove + */ + property( + const property& p, + const string& newDefaultValue, + const int addFlags = FLAG_NONE, + const int removeFlags = FLAG_NONE + ); + + property& operator=(const property& p); + + /** Return the name of the property. + * + * @return property name + */ + const string& getName() const; + + /** Return the default value of the property or + * an empty string if there is no default value. + * + * @return default value for the property + */ + const string& getDefaultValue() const; + + /** Return the value type of the property. + * + * @return property value type + */ + Types getType() const; + + /** Return the attributes of the property (see + * serviceInfos::property::Types constants). + * + * @return property attributes + */ + int getFlags() const; + + private: + + string m_name; + string m_defaultValue; + Types m_type; + int m_flags; + }; + + + /** Return the property prefix used by this service. + * Use this to set/get properties in the session object. + * + * @return property prefix + */ + virtual const string getPropertyPrefix() const = 0; + + /** Return a list of available properties for this service. + * + * @return list of properties + */ + virtual const std::vector <property> getAvailableProperties() const = 0; + + /** Helper function to retrieve the value of a property. + * + * @param s session object + * @param p property to retrieve + * @throw exceptions::no_such_property if the property does not exist + * and has the flag property::FLAG_REQUIRED + * @return value of the property + */ + template <typename TYPE> + const TYPE getPropertyValue(const shared_ptr <session>& s, const property& p) const { + + if (p.getFlags() & property::FLAG_REQUIRED) { + return s->getProperties()[getPropertyPrefix() + p.getName()].template getValue <TYPE>(); + } + + return s->getProperties().template getProperty <TYPE>( + getPropertyPrefix() + p.getName(), + propertySet::valueFromString <TYPE>(p.getDefaultValue()) + ); + } + + /** Helper function to test if the specified property is set in + * the session object. + * + * @param s session object + * @param p property to test + * @return true if the property is set, false otherwise + */ + bool hasProperty(const shared_ptr <session>& s, const property& p) const; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SERVICEINFOS_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/serviceRegistration.inl b/vmime-master/src/vmime/net/serviceRegistration.inl new file mode 100644 index 0000000..9365199 --- /dev/null +++ b/vmime-master/src/vmime/net/serviceRegistration.inl @@ -0,0 +1,105 @@ +// +// 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/net/serviceFactory.hpp" + + +#ifndef VMIME_BUILDING_DOC + + +namespace vmime { +namespace net { + + +template <class S> +class registeredServiceImpl : public serviceFactory::registeredService { + +public: + + registeredServiceImpl( + const string& name, + const int type + ) + : m_type(type), + m_name(name), + m_servInfos(S::getInfosInstance()) { + + } + + shared_ptr <service> create( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ) const { + + return make_shared <S>(sess, auth); + } + + const serviceInfos& getInfos() const { + + return m_servInfos; + } + + const string& getName() const { + + return m_name; + } + + int getType() const { + + return m_type; + } + +private: + + const int m_type; + const string m_name; + const serviceInfos& m_servInfos; +}; + + +// Basic service registerer +template <class S> +class serviceRegisterer { + +public: + + serviceRegisterer(const string& protocol, const service::Type type) { + + serviceFactory::getInstance()->registerService( + make_shared <registeredServiceImpl <S> >(protocol, type) + ); + } +}; + + +} // net +} // vmime + + +#define REGISTER_SERVICE(p_class, p_name, p_type) \ + vmime::net::serviceRegisterer <vmime::net::p_class> \ + p_name(#p_name, vmime::net::service::p_type) + + +#endif // VMIME_BUILDING_DOC + diff --git a/vmime-master/src/vmime/net/session.cpp b/vmime-master/src/vmime/net/session.cpp new file mode 100644 index 0000000..d4b13b4 --- /dev/null +++ b/vmime-master/src/vmime/net/session.cpp @@ -0,0 +1,188 @@ +// +// 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 + + +#include "vmime/net/session.hpp" +#include "vmime/net/serviceFactory.hpp" + +#include "vmime/net/store.hpp" +#include "vmime/net/transport.hpp" + + +namespace vmime { +namespace net { + + +session::session() { + +#if VMIME_HAVE_TLS_SUPPORT + m_tlsProps = make_shared <tls::TLSProperties>(); +#endif // VMIME_HAVE_TLS_SUPPORT + +} + + +session::session(const propertySet& props) + : m_props(props) { + +#if VMIME_HAVE_TLS_SUPPORT + m_tlsProps = make_shared <tls::TLSProperties>(); +#endif // VMIME_HAVE_TLS_SUPPORT + +} + + +session::~session() { + +} + + +// static +shared_ptr <session> session::create() { + + return shared_ptr <session>(new session()); +} + + +// static +shared_ptr <session> session::create(const propertySet& props) { + + return shared_ptr <session>(new session(props)); +} + + +shared_ptr <transport> session::getTransport(const shared_ptr <security::authenticator>& auth) { + + return getTransport(m_props["transport.protocol"], auth); +} + + +shared_ptr <transport> session::getTransport( + const string& protocol, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <session> sess(dynamicCast <session>(shared_from_this())); + shared_ptr <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth); + + if (!sv || sv->getType() != service::TYPE_TRANSPORT) { + return null; + } + + return dynamicCast <transport>(sv); +} + + +shared_ptr <transport> session::getTransport( + const utility::url& url, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <session> sess(dynamicCast <session>(shared_from_this())); + shared_ptr <service> sv = serviceFactory::getInstance()->create(sess, url, auth); + + if (!sv || sv->getType() != service::TYPE_TRANSPORT) { + return null; + } + + return dynamicCast <transport>(sv); +} + + +shared_ptr <store> session::getStore(const shared_ptr <security::authenticator>& auth) { + + return getStore(m_props["store.protocol"], auth); +} + + +shared_ptr <store> session::getStore( + const string& protocol, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <session> sess(dynamicCast <session>(shared_from_this())); + shared_ptr <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth); + + if (!sv || sv->getType() != service::TYPE_STORE) { + return null; + } + + return dynamicCast <store>(sv); +} + + +shared_ptr <store> session::getStore( + const utility::url& url, + const shared_ptr <security::authenticator>& auth +) { + + shared_ptr <session> sess(dynamicCast <session>(shared_from_this())); + shared_ptr <service> sv = serviceFactory::getInstance()->create(sess, url, auth); + + if (!sv || sv->getType() != service::TYPE_STORE) { + return null; + } + + return dynamicCast <store>(sv); +} + + +const propertySet& session::getProperties() const { + + return m_props; +} + + +propertySet& session::getProperties() { + + return m_props; +} + + +#if VMIME_HAVE_TLS_SUPPORT + +void session::setTLSProperties(const shared_ptr <tls::TLSProperties>& tlsProps) { + + m_tlsProps = make_shared <tls::TLSProperties>(*tlsProps); +} + + +shared_ptr <tls::TLSProperties> session::getTLSProperties() const { + + return m_tlsProps; +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/session.hpp b/vmime-master/src/vmime/net/session.hpp new file mode 100644 index 0000000..7ccd0be --- /dev/null +++ b/vmime-master/src/vmime/net/session.hpp @@ -0,0 +1,206 @@ +// +// 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. +// + +#ifndef VMIME_NET_SESSION_HPP_INCLUDED +#define VMIME_NET_SESSION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/security/authenticator.hpp" + +#if VMIME_HAVE_TLS_SUPPORT +# include "vmime/net/tls/TLSProperties.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + +#include "vmime/utility/url.hpp" + +#include "vmime/propertySet.hpp" + + +namespace vmime { +namespace net { + + +class store; +class transport; + + +/** An object that contains all the information needed + * for connection to a service. + */ +class VMIME_EXPORT session : public object, public enable_shared_from_this <session> { + +public: + + /** Construct a new session. + * + * @return pointer to a new session + */ + static shared_ptr <session> create(); + + /** Construct a new session given properties. + * + * @param props session properties + * @return pointer to a new session + */ + static shared_ptr <session> create(const propertySet& props); + + ~session(); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "transport.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service, or NULL if no service is registered for this + * protocol or is not a transport protocol + */ + shared_ptr <transport> getTransport( + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return a transport service instance for the specified protocol. + * + * @param protocol transport protocol to use (eg. "smtp") + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service, or NULL if no service is registered for this + * protocol or is not a transport protocol + */ + shared_ptr <transport> getTransport( + const string& protocol, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return a transport service instance for the specified URL. + * + * @param url full URL with at least the protocol to use (eg: "smtp://myserver.com/") + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service, or NULL if no service is registered for this + * protocol or is not a transport protocol + */ + shared_ptr <transport> getTransport( + const utility::url& url, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "store.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new store service, or NULL if no service is registered for this + * protocol or is not a store protocol + */ + shared_ptr <store> getStore( + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return a store service instance for the specified protocol. + * + * @param protocol store protocol to use (eg. "imap") + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new store service, or NULL if no service is registered for this + * protocol or is not a store protocol + */ + shared_ptr <store> getStore( + const string& protocol, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Return a store service instance for the specified URL. + * + * @param url full URL with at least the protocol to use (eg: "imap://username:password@myserver.com/") + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new store service, or NULL if no service is registered for this + * protocol or is not a store protocol + */ + shared_ptr <store> getStore( + const utility::url& url, + const shared_ptr <security::authenticator>& auth = null + ); + + /** Properties for the session and for the services. + */ + const propertySet& getProperties() const; + + /** Properties for the session and for the services. + */ + propertySet& getProperties(); + +#if VMIME_HAVE_TLS_SUPPORT + + /** Set properties for SSL/TLS secured connections in this session. + * + * @param tlsProps SSL/TLS properties + */ + void setTLSProperties(const shared_ptr <tls::TLSProperties>& tlsProps); + + /** Get properties for SSL/TLS secured connections in this session. + * + * @return SSL/TLS properties + */ + shared_ptr <tls::TLSProperties> getTLSProperties() const; + +#endif // VMIME_HAVE_TLS_SUPPORT + +private: + + session(); + session(const propertySet& props); + + + propertySet m_props; + +#if VMIME_HAVE_TLS_SUPPORT + shared_ptr <tls::TLSProperties> m_tlsProps; +#endif // VMIME_HAVE_TLS_SUPPORT + +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SESSION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp new file mode 100644 index 0000000..3b242d1 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp @@ -0,0 +1,180 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp" + +#include "vmime/net/smtp/SMTPConnection.hpp" +#include "vmime/net/smtp/SMTPTransport.hpp" + +#include <algorithm> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPChunkingOutputStreamAdapter::SMTPChunkingOutputStreamAdapter( + const shared_ptr <SMTPConnection>& conn, + const size_t size, + utility::progressListener* progress +) + : m_connection(conn), + m_bufferSize(0), + m_chunkCount(0), + m_totalSize(size), + m_totalSent(0), + m_progress(progress) { + + if (progress) { + progress->start(size); + } +} + + +void SMTPChunkingOutputStreamAdapter::sendChunk( + const byte_t* const data, + const size_t count, + const bool last +) { + + if (count == 0 && !last) { + // Nothing to send + return; + } + + // Send this chunk + m_connection->sendRequest(SMTPCommand::BDAT(count, last)); + m_connection->getSocket()->sendRaw(data, count); + + ++m_chunkCount; + + if (m_progress) { + + m_totalSent += count; + m_totalSize = std::max(m_totalSize, m_totalSent); + + m_progress->progress(m_totalSent, m_totalSize); + } + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(count); + } + + // If PIPELINING is not supported, read one response for this BDAT command + if (!m_connection->hasExtension("PIPELINING")) { + + shared_ptr <SMTPResponse> resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + m_connection->getTransport()->disconnect(); + throw exceptions::command_error("BDAT", resp->getText()); + } + + // If PIPELINING is supported, read one response for each chunk (ie. number + // of BDAT commands issued) after the last chunk has been sent + } else if (last) { + + bool invalidReply = false; + shared_ptr <SMTPResponse> resp; + + for (unsigned int i = 0 ; i < m_chunkCount ; ++i) { + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + invalidReply = true; + } + } + + if (invalidReply) { + m_connection->getTransport()->disconnect(); + throw exceptions::command_error("BDAT", resp->getText()); + } + } +} + + +void SMTPChunkingOutputStreamAdapter::writeImpl( + const byte_t* const data, + const size_t count +) { + + const byte_t* curData = data; + size_t curCount = count; + + while (curCount != 0) { + + // Fill the buffer + const size_t remaining = sizeof(m_buffer) - m_bufferSize; + const size_t bytesToCopy = std::min(remaining, curCount); + + std::copy(curData, curData + bytesToCopy, m_buffer + m_bufferSize); + + m_bufferSize += bytesToCopy; + curData += bytesToCopy; + curCount -= bytesToCopy; + + // If the buffer is full, send this chunk + if (m_bufferSize >= sizeof(m_buffer)) { + + sendChunk(m_buffer, m_bufferSize, /* last */ false); + m_bufferSize = 0; + } + } +} + + +void SMTPChunkingOutputStreamAdapter::flush() { + + sendChunk(m_buffer, m_bufferSize, /* last */ true); + m_bufferSize = 0; + + if (m_progress) { + m_progress->stop(m_totalSize); + } + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(m_bufferSize); + } +} + + +size_t SMTPChunkingOutputStreamAdapter::getBlockSize() { + + return sizeof(m_buffer); +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp new file mode 100644 index 0000000..4ef466a --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp @@ -0,0 +1,94 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/utility/outputStream.hpp" +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +class SMTPConnection; + + +/** An output stream adapter used with ESMTP CHUNKING extension. + */ +class VMIME_EXPORT SMTPChunkingOutputStreamAdapter : public utility::outputStream { + +public: + + SMTPChunkingOutputStreamAdapter( + const shared_ptr <SMTPConnection>& conn, + const size_t size, + utility::progressListener* progress + ); + + void flush(); + + size_t getBlockSize(); + +protected: + + void writeImpl(const byte_t* const data, const size_t count); + +private: + + SMTPChunkingOutputStreamAdapter(const SMTPChunkingOutputStreamAdapter&); + + + void sendChunk(const byte_t* const data, const size_t count, const bool last); + + + shared_ptr <SMTPConnection> m_connection; + + byte_t m_buffer[262144]; // 256 KB + size_t m_bufferSize; + + unsigned int m_chunkCount; + + size_t m_totalSize; + size_t m_totalSent; + utility::progressListener* m_progress; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp b/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp new file mode 100644 index 0000000..5e533d1 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommand.cpp @@ -0,0 +1,258 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPCommand.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/mailbox.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPCommand::SMTPCommand(const string& text, const string& traceText) + : m_text(text), + m_traceText(traceText) +{ +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::EHLO(const string& hostname) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "EHLO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::HELO(const string& hostname) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "HELO " << hostname; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::AUTH(const string& mechName) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::AUTH(const string& mechName, const std::string& initialResponse) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::STARTTLS() { + + return createCommand("STARTTLS"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, + const std::string& dsnRet, const std::string& dsnEnvelopId) { + + return MAIL(mbox, utf8, 0, dsnRet, dsnEnvelopId); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const size_t size, + const std::string& dsnRet, const std::string& dsnEnvelopId) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "MAIL FROM:<"; + + if (utf8) { + + cmd << mbox.getEmail().toText().getConvertedText(vmime::charsets::UTF_8); + + } else { + + vmime::utility::outputStreamAdapter cmd2(cmd); + mbox.getEmail().generate(cmd2); + } + + cmd << ">"; + + if (!dsnRet.empty()) { + cmd << " " << dsn::RET << "=" << dsnRet; + } + if (!dsnEnvelopId.empty()) { + cmd << " " << dsn::ENVID << "=<" << dsnEnvelopId << ">"; + } + + if (utf8) { + cmd << " SMTPUTF8"; + } + + if (size != 0) { + cmd << " SIZE=" << size; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8, + const string& dsnNotify) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RCPT TO:<"; + + if (utf8) { + + cmd << mbox.getEmail().toText().getConvertedText(vmime::charsets::UTF_8); + + } else { + + vmime::utility::outputStreamAdapter cmd2(cmd); + mbox.getEmail().generate(cmd2); + } + + cmd << ">"; + + if (!dsnNotify.empty()) { + cmd << " " << dsn::NOTIFY << "=" << dsnNotify; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::RSET() { + + return createCommand("RSET"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::DATA() { + + return createCommand("DATA"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::BDAT(const size_t chunkSize, const bool last) { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "BDAT " << chunkSize; + + if (last) { + cmd << " LAST"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::NOOP() { + + return createCommand("NOOP"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::QUIT() { + + return createCommand("QUIT"); +} + + +// static +shared_ptr <SMTPCommand> SMTPCommand::createCommand(const string& text, const string& traceText) { + + if (traceText.empty()) { + return shared_ptr <SMTPCommand>(new SMTPCommand(text, text)); + } else { + return shared_ptr <SMTPCommand>(new SMTPCommand(text, traceText)); + } +} + + +const string SMTPCommand::getText() const { + + return m_text; +} + + +const string SMTPCommand::getTraceText() const { + + return m_traceText; +} + + +void SMTPCommand::writeToSocket(const shared_ptr <socket>& sok, shared_ptr <tracer> tr) { + + sok->send(m_text + "\r\n"); + + if (tr) { + tr->traceSend(m_traceText); + } +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp b/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp new file mode 100644 index 0000000..9a32d1e --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommand.hpp @@ -0,0 +1,125 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" + + +namespace vmime { + + +class mailbox; + + +namespace net { + + +class socket; +class timeoutHandler; +class tracer; + + +namespace smtp { + + +/** A SMTP command, as sent to server. + */ +class VMIME_EXPORT SMTPCommand : public object { + +public: + + static shared_ptr <SMTPCommand> HELO(const string& hostname); + static shared_ptr <SMTPCommand> EHLO(const string& hostname); + static shared_ptr <SMTPCommand> AUTH(const string& mechName); + static shared_ptr <SMTPCommand> AUTH(const string& mechName, const std::string& initialResponse); + static shared_ptr <SMTPCommand> STARTTLS(); + static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, + const std::string& dsnRet, const std::string& dsnEnvelopId); + static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size, + const std::string& dsnRet, const std::string& dsnEnvelopId); + static shared_ptr <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8, + const std::string& dsnNotify); + static shared_ptr <SMTPCommand> RSET(); + static shared_ptr <SMTPCommand> DATA(); + static shared_ptr <SMTPCommand> BDAT(const size_t chunkSize, const bool last); + static shared_ptr <SMTPCommand> NOOP(); + static shared_ptr <SMTPCommand> QUIT(); + + /** Creates a new SMTP command with the specified text. + * + * @param text command text + * @return a new SMTPCommand object + */ + static shared_ptr <SMTPCommand> createCommand(const string& text, const string& traceText = ""); + + /** Sends this command to the specified socket. + * + * @param sok socket to which the command will be written + * @param tr tracer + */ + virtual void writeToSocket(const shared_ptr <socket>& sok, shared_ptr <tracer> tr); + + /** Returns the full text of the command, including command name + * and parameters (if any). + * + * @return command text (eg. "RCPT TO:<vincent@kisli.com>") + */ + virtual const string getText() const; + + /** Returns the full text of the command, suitable for outputing + * to the tracer. + * + * @return trace text (eg. "LOGIN myusername ***") + */ + virtual const string getTraceText() const; + +protected: + + SMTPCommand(const string& text, const string& traceText); + SMTPCommand(const SMTPCommand&); + +private: + + string m_text; + string m_traceText; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp new file mode 100644 index 0000000..eab7086 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.cpp @@ -0,0 +1,160 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPCommandSet.hpp" + +#include "vmime/net/socket.hpp" + +#include "vmime/mailbox.hpp" + +#include <stdexcept> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPCommandSet::SMTPCommandSet(const bool pipeline) + : SMTPCommand("", ""), + m_pipeline(pipeline), + m_started(false), + m_lastCommandSent() { + +} + + +// static +shared_ptr <SMTPCommandSet> SMTPCommandSet::create(const bool pipeline) { + + return shared_ptr <SMTPCommandSet>(new SMTPCommandSet(pipeline)); +} + + +void SMTPCommandSet::addCommand(const shared_ptr <SMTPCommand>& cmd) { + + if (m_started) { + throw std::runtime_error("Could not add command to pipeline: " + "one or more commands have already been sent to the server."); + } + + m_commands.push_back(cmd); +} + + +void SMTPCommandSet::writeToSocket(const shared_ptr <socket>& sok, const shared_ptr <tracer>& tr) { + + if (m_pipeline) { + + if (!m_started) { + + // Send all commands at once + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + shared_ptr <SMTPCommand> cmd = *it; + cmd->writeToSocket(sok, tr); + } + } + + if (!m_commands.empty()) { + + // Advance the pointer to last command sent + shared_ptr <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + m_lastCommandSent = cmd; + } + + } else { + + if (!m_commands.empty()) { + + // Send only one command + shared_ptr <SMTPCommand> cmd = m_commands.front(); + m_commands.pop_front(); + + cmd->writeToSocket(sok, tr); + + m_lastCommandSent = cmd; + } + } + + m_started = true; +} + + +const string SMTPCommandSet::getText() const { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + cmd << (*it)->getText() << "\r\n"; + } + + return cmd.str(); +} + + +const string SMTPCommandSet::getTraceText() const { + + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + for (std::list <shared_ptr <SMTPCommand> >::const_iterator it = m_commands.begin() ; + it != m_commands.end() ; ++it) { + + cmd << (*it)->getTraceText() << "\r\n"; + } + + return cmd.str(); +} + + +bool SMTPCommandSet::isFinished() const { + + return (m_pipeline && m_started) || (m_commands.size() == 0 && m_started); +} + + +shared_ptr <SMTPCommand> SMTPCommandSet::getLastCommandSent() const { + + return m_lastCommandSent; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp new file mode 100644 index 0000000..2fd977c --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPCommandSet.hpp @@ -0,0 +1,106 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include <list> + +#include "vmime/net/smtp/SMTPCommand.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** A set of SMTP commands, which may be sent all at once + * to the server if pipelining is supported. + */ +class VMIME_EXPORT SMTPCommandSet : public SMTPCommand { + +public: + + /** Creates a new set of SMTP commands. + * + * @param pipeline set to true if the server supports pipelining + * @return a new SMTPCommandSet object + */ + static shared_ptr <SMTPCommandSet> create(const bool pipeline); + + /** Adds a new command to this set. + * If one or more comments have already been sent to the server, + * an exception will be thrown. + * + * @param cmd command to add + */ + void addCommand(const shared_ptr <SMTPCommand>& cmd); + + /** Tests whether all commands have been sent. + * + * @return true if all commands have been sent, + * or false otherwise + */ + bool isFinished() const; + + /** Returns the last command which has been sent. + * + * @return a pointer to a SMTPCommand, of NULL if no command + * has been sent yet + */ + shared_ptr <SMTPCommand> getLastCommandSent() const; + + + void writeToSocket(const shared_ptr <socket>& sok, const shared_ptr <tracer>& tr); + + const string getText() const; + const string getTraceText() const; + +private: + + SMTPCommandSet(const bool pipeline); + SMTPCommandSet(const SMTPCommandSet&); + + + bool m_pipeline; + bool m_started; + std::list <shared_ptr <SMTPCommand> > m_commands; + shared_ptr <SMTPCommand> m_lastCommandSent; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCOMMANDSET_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPConnection.cpp b/vmime-master/src/vmime/net/smtp/SMTPConnection.cpp new file mode 100644 index 0000000..07d0376 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPConnection.cpp @@ -0,0 +1,714 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPConnection.hpp" +#include "vmime/net/smtp/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platform.hpp" + +#include "vmime/security/digest/messageDigestFactory.hpp" + +#include "vmime/net/defaultConnectionInfos.hpp" + +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/SASLContext.hpp" +#else + #include "vmime/utility/encoder/b64Encoder.hpp" + #include "vmime/utility/inputStreamStringAdapter.hpp" + #include "vmime/utility/outputStreamStringAdapter.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" + #include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + + +// Helpers for service properties +#define GET_PROPERTY(type, prop) \ + (m_transport.lock()->getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const SMTPServiceInfos&>(m_transport.lock()->getInfos()).getProperties().prop)) +#define HAS_PROPERTY(prop) \ + (m_transport.lock()->getInfos().hasProperty(getSession(), \ + dynamic_cast <const SMTPServiceInfos&>(m_transport.lock()->getInfos()).getProperties().prop)) + + +namespace vmime { +namespace net { +namespace smtp { + + + +SMTPConnection::SMTPConnection( + const shared_ptr <SMTPTransport>& transport, + const shared_ptr <security::authenticator>& auth +) + : m_transport(transport), + m_auth(auth), + m_socket(null), + m_timeoutHandler(null), + m_authenticated(false), + m_secured(false), + m_extendedSMTP(false) { + + static int connectionId = 0; + + if (transport->getTracerFactory()) { + m_tracer = transport->getTracerFactory()->create(transport, ++connectionId); + } +} + + +SMTPConnection::~SMTPConnection() { + + try { + + if (isConnected()) { + disconnect(); + } else if (m_socket) { + internalDisconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +void SMTPConnection::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS); + const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT); + + shared_ptr <SMTPTransport> transport = m_transport.lock(); + + // Create the time-out handler + if (transport->getTimeoutHandlerFactory()) { + m_timeoutHandler = transport->getTimeoutHandlerFactory()->create(); + } + + // Create and connect the socket + m_socket = transport->getSocketFactory()->create(m_timeoutHandler); + m_socket->setTracer(m_tracer); + +#if VMIME_HAVE_TLS_SUPPORT + if (transport->isSMTPS()) { // dedicated port/SMTPS + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + transport->getCertificateVerifier(), + transport->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket); + + } else +#endif // VMIME_HAVE_TLS_SUPPORT + { + m_cntInfos = make_shared <defaultConnectionInfos>(address, port); + } + + m_socket->connect(address, port); + + // Connection + // + // eg: C: <connection to server> + // --- S: 220 smtp.domain.com Service ready + + shared_ptr <SMTPResponse> resp; + + if ((resp = readResponse())->getCode() != 220) { + internalDisconnect(); + throw exceptions::connection_greeting_error(resp->getText()); + } + + // Identification + helo(); + +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!transport->isSMTPS() && tls) { // only if not SMTPS + + try { + + startTLS(); + + // Non-fatal error + } catch (exceptions::command_error&) { + + if (tlsRequired) { + throw; + } else { + // TLS is not required, so don't bother + } + + // Fatal error + } catch (...) { + + throw; + } + + // Must reissue a EHLO command [RFC-2487, 5.2] + helo(); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH)) { + authenticate(); + } else { + m_authenticated = true; + } +} + + +void SMTPConnection::helo() { + + // First, try Extended SMTP (ESMTP) + // + // eg: C: EHLO thismachine.ourdomain.com + // S: 250-smtp.theserver.com + // S: 250-AUTH CRAM-MD5 DIGEST-MD5 + // S: 250-PIPELINING + // S: 250 SIZE 2555555555 + + sendRequest(SMTPCommand::EHLO(platform::getHandler()->getHostName())); + + shared_ptr <SMTPResponse> resp; + + if ((resp = readResponse())->getCode() != 250) { + + // Next, try "Basic" SMTP + // + // eg: C: HELO thismachine.ourdomain.com + // S: 250 OK + + sendRequest(SMTPCommand::HELO(platform::getHandler()->getHostName())); + + if ((resp = readResponse())->getCode() != 250) { + internalDisconnect(); + throw exceptions::connection_greeting_error(resp->getLastLine().getText()); + } + + m_extendedSMTP = false; + m_extensions.clear(); + + } else { + + m_extendedSMTP = true; + m_extensions.clear(); + + // Get supported extensions from SMTP response + // One extension per line, format is: EXT PARAM1 PARAM2... + for (size_t i = 1, n = resp->getLineCount() ; i < n ; ++i) { + + const string line = resp->getLineAt(i).getText(); + + std::istringstream iss(line); + iss.imbue(std::locale::classic()); + + string ext; + iss >> ext; + + std::vector <string> params; + string param; + + // Special case: some servers send "AUTH=MECH [MECH MECH...]" + if (ext.length() >= 5 && utility::stringUtils::toUpper(ext.substr(0, 5)) == "AUTH=") { + + params.push_back(utility::stringUtils::toUpper(ext.substr(5))); + ext = "AUTH"; + } + + while (iss >> param) { + params.push_back(utility::stringUtils::toUpper(param)); + } + + m_extensions[ext] = params; + } + } +} + + +bool SMTPConnection::hasExtension( + const std::string& extName, + std::vector <string>* params +) const { + + std::map <string, std::vector <string> >::const_iterator + it = m_extensions.find(extName); + + if (it != m_extensions.end()) { + + if (params) { + *params = (*it).second; + } + + return true; + + } else { + + return false; + } +} + + +void SMTPConnection::authenticate() { + + if (!m_extendedSMTP) { + internalDisconnect(); + throw exceptions::command_error("AUTH", "ESMTP not supported."); + } + + getAuthenticator()->setService(m_transport.lock()); + +#if VMIME_HAVE_SASL_SUPPORT + // Try SASL authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) { + + try { + + authenticateSASL(); + + m_authenticated = true; + return; + + } catch (exception&) { + + internalDisconnect(); + throw; + } + } +#else // no SASL + + // allow AUTH PLAIN over TLS - it is a popular and simple mechanism + if (m_secured) { + + std::vector <string> authMechs; + hasExtension("AUTH", &authMechs); + + if (authMechs.empty()) { + throw exceptions::authentication_error("No AUTH mechanism available."); + } + + const string plain("PLAIN"); + + if (std::find(authMechs.begin(), authMechs.end(), plain) != authMechs.end()) { + + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); + const string authToken = username + '\0' + username + '\0' + password; + + utility::inputStreamStringAdapter in(authToken); + string authTokenBase64; + utility::outputStreamStringAdapter out(authTokenBase64); + + vmime::utility::encoder::b64Encoder encoder; + encoder.encode(in, out); + + sendRequest(SMTPCommand::AUTH(plain, authTokenBase64)); + + shared_ptr <SMTPResponse> response = readResponse(); + + const int code = response ? response->getCode() : -1; + + if (code == 235) { + m_authenticated = true; + return; + } + } + } + +#endif // VMIME_HAVE_SASL_SUPPORT + + // No other authentication method is possible + throw exceptions::authentication_error("All authentication methods failed"); +} + + + +#if VMIME_HAVE_SASL_SUPPORT + +void SMTPConnection::authenticateSASL() { + + if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) { + throw exceptions::authentication_error("No SASL authenticator available."); + } + + // Obtain SASL mechanisms supported by server from ESMTP extensions + std::vector <string> saslMechs; + hasExtension("AUTH", &saslMechs); + + if (saslMechs.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; + + shared_ptr <security::sasl::SASLContext> saslContext = + security::sasl::SASLContext::create(); + + for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { + + try { + + mechList.push_back + (saslContext->createMechanism(saslMechs[i])); + + } catch (exceptions::no_such_mechanism&) { + + // Ignore mechanism + } + } + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try to suggest a mechanism among all those supported + shared_ptr <security::sasl::SASLMechanism> suggestedMech = + saslContext->suggestMechanism(mechList); + + if (!suggestedMech) { + throw exceptions::authentication_error("Unable to suggest SASL mechanism."); + } + + // Allow application to choose which mechanisms to use + mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> + getAcceptableMechanisms(mechList, suggestedMech); + + if (mechList.empty()) { + throw exceptions::authentication_error("No SASL mechanism available."); + } + + // Try each mechanism in the list in turn + for (unsigned int i = 0 ; i < mechList.size() ; ++i) { + + shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; + + shared_ptr <security::sasl::SASLSession> saslSession = + saslContext->createSession("smtp", getAuthenticator(), mech); + + saslSession->init(); + + if (saslSession->getMechanism()->hasInitialResponse()) { + + byte_t* initialResp = 0; + size_t initialRespLen = 0; + + saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); + + string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); + delete [] initialResp; + + if (encodedInitialResp.empty()) { + sendRequest(SMTPCommand::AUTH(mech->getName(), "=")); + } else { + sendRequest(SMTPCommand::AUTH(mech->getName(), encodedInitialResp)); + } + + } else { + + sendRequest(SMTPCommand::AUTH(mech->getName())); + } + + for (bool cont = true ; cont ; ) { + + shared_ptr <SMTPResponse> response = readResponse(); + + switch (response->getCode()) { + + case 235: { + + m_socket = saslSession->getSecuredSocket(m_socket); + return; + } + case 334: { + + byte_t* challenge = 0; + size_t challengeLen = 0; + + byte_t* resp = 0; + size_t respLen = 0; + + try { + + // Extract challenge + saslContext->decodeB64(response->getText(), &challenge, &challengeLen); + + // Prepare response + saslSession->evaluateChallenge(challenge, challengeLen, &resp, &respLen); + + // Send response + const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; + m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); + + if (m_tracer) { + m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); + } + + } catch (exceptions::sasl_exception& e) { + + if (challenge) { + delete [] challenge; + challenge = NULL; + } + + if (resp) { + delete [] resp; + resp = NULL; + } + + // Cancel SASL exchange + m_socket->send("*\r\n"); + + if (m_tracer) { + m_tracer->traceSend("*"); + } + + } catch (...) { + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + throw; + } + + if (challenge) { + delete [] challenge; + } + + if (resp) { + delete [] resp; + } + + break; + } + default: + + cont = false; + break; + } + } + } + + throw exceptions::authentication_error("Could not authenticate using SASL: all mechanisms failed."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +#if VMIME_HAVE_TLS_SUPPORT + +void SMTPConnection::startTLS() { + + try { + + sendRequest(SMTPCommand::STARTTLS()); + + shared_ptr <SMTPResponse> resp = readResponse(); + + if (resp->getCode() != 220) { + + throw SMTPCommandError( + "STARTTLS", resp->getText(), resp->getCode(), resp->getEnhancedCode() + ); + } + + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( + getTransport()->getCertificateVerifier(), + getTransport()->getSession()->getTLSProperties() + ); + + shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); + + tlsSocket->handshake(); + + m_socket = tlsSocket; + + m_secured = true; + m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>( + m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket + ); + + } catch (exceptions::command_error&) { + + // Non-fatal error + throw; + + } catch (exception&) { + + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + +void SMTPConnection::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + internalDisconnect(); +} + + +void SMTPConnection::internalDisconnect() { + + if (isConnected()) { + + try { + + sendRequest(SMTPCommand::QUIT()); + + // Do not wait for server response. This is contrary to the RFC, but + // some servers never send a response to a QUIT command. + + } catch (exception&) { + + // Not important + } + } + + m_socket->disconnect(); + m_socket = null; + + m_timeoutHandler = null; + + m_authenticated = false; + m_extendedSMTP = false; + + m_secured = false; + m_cntInfos = null; +} + + +void SMTPConnection::sendRequest(const shared_ptr <SMTPCommand>& cmd) { + + cmd->writeToSocket(m_socket, m_tracer); +} + + +shared_ptr <SMTPResponse> SMTPConnection::readResponse() { + + shared_ptr <SMTPResponse> resp = SMTPResponse::readResponse( + m_tracer, m_socket, m_timeoutHandler, m_responseState + ); + + m_responseState = resp->getCurrentState(); + + return resp; +} + + +bool SMTPConnection::isConnected() const { + + return m_socket && m_socket->isConnected() && m_authenticated; +} + + +bool SMTPConnection::isSecuredConnection() const { + + return m_secured; +} + + +shared_ptr <connectionInfos> SMTPConnection::getConnectionInfos() const { + + return m_cntInfos; +} + + +shared_ptr <SMTPTransport> SMTPConnection::getTransport() { + + return m_transport.lock(); +} + + +shared_ptr <session> SMTPConnection::getSession() { + + return m_transport.lock()->getSession(); +} + + +shared_ptr <socket> SMTPConnection::getSocket() { + + return m_socket; +} + + +shared_ptr <tracer> SMTPConnection::getTracer() { + + return m_tracer; +} + + +shared_ptr <timeoutHandler> SMTPConnection::getTimeoutHandler() { + + return m_timeoutHandler; +} + + +shared_ptr <security::authenticator> SMTPConnection::getAuthenticator() { + + return m_auth; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp b/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp new file mode 100644 index 0000000..d8a2375 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPConnection.hpp @@ -0,0 +1,136 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPCONNECTION_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPCONNECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/messageId.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/session.hpp" +#include "vmime/net/connectionInfos.hpp" +#include "vmime/net/tracer.hpp" + +#include "vmime/net/smtp/SMTPCommand.hpp" +#include "vmime/net/smtp/SMTPResponse.hpp" + +#include "vmime/security/authenticator.hpp" + + +namespace vmime { +namespace net { + + +class socket; +class timeoutHandler; + + +namespace smtp { + + +class SMTPTransport; + + +/** Manage connection to a SMTP server. + */ +class VMIME_EXPORT SMTPConnection : public object { + +public: + + SMTPConnection( + const shared_ptr <SMTPTransport>& transport, + const shared_ptr <security::authenticator>& auth + ); + + ~SMTPConnection(); + + + virtual void connect(); + virtual bool isConnected() const; + virtual void disconnect(); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + + virtual shared_ptr <SMTPTransport> getTransport(); + virtual shared_ptr <socket> getSocket(); + virtual shared_ptr <timeoutHandler> getTimeoutHandler(); + virtual shared_ptr <security::authenticator> getAuthenticator(); + virtual shared_ptr <session> getSession(); + virtual shared_ptr <tracer> getTracer(); + + void sendRequest(const shared_ptr <SMTPCommand>& cmd); + shared_ptr <SMTPResponse> readResponse(); + + bool hasExtension(const std::string& extName, std::vector <string>* params = NULL) const; + +private: + + void internalDisconnect(); + + void helo(); + void authenticate(); +#if VMIME_HAVE_SASL_SUPPORT + void authenticateSASL(); +#endif // VMIME_HAVE_SASL_SUPPORT + +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + + + weak_ptr <SMTPTransport> m_transport; + + shared_ptr <security::authenticator> m_auth; + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + SMTPResponse::state m_responseState; + + bool m_authenticated; + bool m_secured; + + shared_ptr <connectionInfos> m_cntInfos; + + bool m_extendedSMTP; + std::map <string, std::vector <string> > m_extensions; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPCONNECTION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp b/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp new file mode 100644 index 0000000..e3b4652 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPExceptions.cpp @@ -0,0 +1,212 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPExceptions.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +// +// SMTPCommandError +// + +SMTPCommandError::SMTPCommandError( + const string& command, + const string& response, + const string& desc, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other +) + : command_error(command, response, desc, other), + m_status(statusCode), + m_exStatus(extendedStatusCode) { + +} + + +SMTPCommandError::SMTPCommandError( + const string& command, + const string& response, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other +) + : command_error(command, response, "", other), + m_status(statusCode), + m_exStatus(extendedStatusCode) { + +} + + +SMTPCommandError::~SMTPCommandError() throw() { + +} + + +int SMTPCommandError::statusCode() const { + + return m_status; +} + + +const SMTPResponse::enhancedStatusCode SMTPCommandError::extendedStatusCode() const { + + return m_exStatus; +} + + +exception* SMTPCommandError::clone() const { + + return new SMTPCommandError(*this); +} + + +const char* SMTPCommandError::name() const throw() { + + return "SMTPCommandError"; +} + + +// +// SMTPMessageSizeExceedsMaxLimitsException +// + +SMTPMessageSizeExceedsMaxLimitsException::SMTPMessageSizeExceedsMaxLimitsException(const exception& other) + : net_exception("Message size exceeds maximum server limits (permanent error).", other) { + +} + + +SMTPMessageSizeExceedsMaxLimitsException::~SMTPMessageSizeExceedsMaxLimitsException() throw() { + +} + + +exception* SMTPMessageSizeExceedsMaxLimitsException::clone() const { + + return new SMTPMessageSizeExceedsMaxLimitsException(*this); +} + + +const char* SMTPMessageSizeExceedsMaxLimitsException::name() const throw() { + + return "SMTPMessageSizeExceedsMaxLimitsException"; +} + + +// +// SMTPMessageSizeExceedsCurLimitsException +// + +SMTPMessageSizeExceedsCurLimitsException::SMTPMessageSizeExceedsCurLimitsException(const exception& other) + : net_exception("Message size exceeds current server limits (temporary storage error).", other) { + +} + + +SMTPMessageSizeExceedsCurLimitsException::~SMTPMessageSizeExceedsCurLimitsException() throw() { + +} + + +exception* SMTPMessageSizeExceedsCurLimitsException::clone() const { + + return new SMTPMessageSizeExceedsCurLimitsException(*this); +} + + +const char* SMTPMessageSizeExceedsCurLimitsException::name() const throw() { + + return "SMTPMessageSizeExceedsCurLimitsException"; +} + + +// +// SMTPExtensionNotSupportedException +// + +SMTPExtensionNotSupportedException::SMTPExtensionNotSupportedException(const string &what, const exception& other) + : net_exception(what.empty() ? "A required extension is not supported by the SMTP server." : what, other) { + +} + + +SMTPExtensionNotSupportedException::~SMTPExtensionNotSupportedException() throw() { + +} + + +exception* SMTPExtensionNotSupportedException::clone() const { + + return new SMTPExtensionNotSupportedException(*this); +} + + +const char* SMTPExtensionNotSupportedException::name() const throw() { + + return "SMTPExtensionNotSupportedException"; +} + + +// +// SMTPDSNExtensionNotSupportedException +// + +SMTPDSNExtensionNotSupportedException::SMTPDSNExtensionNotSupportedException(const exception& other) + : SMTPExtensionNotSupportedException("RFC-1891 DSN extension is not supported by the SMTP server.", other) { + +} + + +SMTPDSNExtensionNotSupportedException::~SMTPDSNExtensionNotSupportedException() throw() { + +} + + +exception* SMTPDSNExtensionNotSupportedException::clone() const { + return new SMTPDSNExtensionNotSupportedException(*this); +} + + +const char* SMTPDSNExtensionNotSupportedException::name() const throw() { + + return "SMTPDSNExtensionNotSupportedException"; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP diff --git a/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp b/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp new file mode 100644 index 0000000..488ee2e --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPExceptions.hpp @@ -0,0 +1,159 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/exception.hpp" +#include "vmime/base.hpp" + +#include "vmime/net/smtp/SMTPResponse.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** SMTP Command error: a SMTP command failed. + */ +class VMIME_EXPORT SMTPCommandError : public exceptions::command_error { + +public: + + SMTPCommandError( + const string& command, + const string& response, + const string& desc, + const int statusCode, + const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other = NO_EXCEPTION + ); + + SMTPCommandError( + const string& command, + const string& response, + const int statusCode, const SMTPResponse::enhancedStatusCode& extendedStatusCode, + const exception& other = NO_EXCEPTION + ); + + ~SMTPCommandError() throw(); + + /** Returns the SMTP status code for this error. + * + * @return status code (protocol-dependent) + */ + int statusCode() const; + + /** Returns the extended status code (following RFC-3463) for this + * error, if available. + * + * @return status code + */ + const SMTPResponse::enhancedStatusCode extendedStatusCode() const; + + + exception* clone() const; + const char* name() const throw(); + +private: + + int m_status; + SMTPResponse::enhancedStatusCode m_exStatus; +}; + + +/** SMTP error: message size exceeds maximum server limits. + * This is a permanent error. + */ +class VMIME_EXPORT SMTPMessageSizeExceedsMaxLimitsException : public exceptions::net_exception { + +public: + + SMTPMessageSizeExceedsMaxLimitsException(const exception& other = NO_EXCEPTION); + ~SMTPMessageSizeExceedsMaxLimitsException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: message size exceeds current server limits. + * This is a temporary error (you may retry later). + */ +class VMIME_EXPORT SMTPMessageSizeExceedsCurLimitsException : public exceptions::net_exception { + +public: + + SMTPMessageSizeExceedsCurLimitsException(const exception& other = NO_EXCEPTION); + ~SMTPMessageSizeExceedsCurLimitsException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: a required extension is not supported by the server. + */ +class VMIME_EXPORT SMTPExtensionNotSupportedException : public exceptions::net_exception { + +public: + + SMTPExtensionNotSupportedException(const string& what, const exception& other = NO_EXCEPTION); + ~SMTPExtensionNotSupportedException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +/** SMTP error: RFC-1891 DSN extension is not supported by the server. + */ +class VMIME_EXPORT SMTPDSNExtensionNotSupportedException : public SMTPExtensionNotSupportedException { + +public: + + SMTPDSNExtensionNotSupportedException(const exception& other = NO_EXCEPTION); + ~SMTPDSNExtensionNotSupportedException() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp b/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp new file mode 100644 index 0000000..2234705 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPResponse.cpp @@ -0,0 +1,366 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPResponse.hpp" + +#include "vmime/platform.hpp" +#include "vmime/utility/stringUtils.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/tracer.hpp" + +#include <cctype> + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPResponse::SMTPResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st +) + : m_socket(sok), + m_timeoutHandler(toh), + m_tracer(tr), + m_responseBuffer(st.responseBuffer), + m_responseContinues(false) { + +} + + +SMTPResponse::SMTPResponse(const SMTPResponse&) + : vmime::object() { + + // Not used +} + + +int SMTPResponse::getCode() const { + + const int firstCode = m_lines[0].getCode(); + + for (unsigned int i = 1 ; i < m_lines.size() ; ++i) { + + // All response codes returned must be equal + // or else this in an error... + if (m_lines[i].getCode() != firstCode) { + return 0; + } + } + + return firstCode; +} + + +const SMTPResponse::enhancedStatusCode SMTPResponse::getEnhancedCode() const { + + return m_lines[m_lines.size() - 1].getEnhancedCode(); +} + + +const string SMTPResponse::getText() const { + + string text = m_lines[0].getText(); + + for (unsigned int i = 1 ; i < m_lines.size() ; ++i) { + + text += '\n'; + text += m_lines[i].getText(); + } + + return text; +} + + +// static +shared_ptr <SMTPResponse> SMTPResponse::readResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st +) { + + shared_ptr <SMTPResponse> resp = + shared_ptr <SMTPResponse>(new SMTPResponse(tr, sok, toh, st)); + + resp->readResponse(); + + return resp; +} + + +void SMTPResponse::readResponse() { + + responseLine line = getNextResponse(); + m_lines.push_back(line); + + while (m_responseContinues) { + line = getNextResponse(); + m_lines.push_back(line); + } +} + + +const string SMTPResponse::readResponseLine() { + + string currentBuffer = m_responseBuffer; + + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + while (true) { + + // Get a line from the response buffer + const size_t lineEnd = currentBuffer.find_first_of('\n'); + + if (lineEnd != string::npos) { + + size_t actualLineEnd = lineEnd; + + if (actualLineEnd != 0 && currentBuffer[actualLineEnd - 1] == '\r') { // CRLF case + actualLineEnd--; + } + + const string line(currentBuffer.begin(), currentBuffer.begin() + actualLineEnd); + + currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1); + m_responseBuffer = currentBuffer; + + if (m_tracer) { + m_tracer->traceReceive(line); + } + + return line; + } + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) { + + if (!m_timeoutHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + m_timeoutHandler->resetTimeOut(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) { // buffer is empty + m_socket->waitForRead(); + continue; + } + + currentBuffer += receiveBuffer; + } +} + + +const SMTPResponse::responseLine SMTPResponse::getNextResponse() { + + string line = readResponseLine(); + + const int code = extractResponseCode(line); + string text; + + m_responseContinues = (line.length() >= 4 && line[3] == '-'); + + if (line.length() > 4) { + text = utility::stringUtils::trim(line.substr(4)); + } else { + text = ""; + } + + return responseLine(code, text, extractEnhancedCode(text)); +} + + +// static +int SMTPResponse::extractResponseCode(const string& response) { + + int code = 0; + + if (response.length() >= 3) { + + code = (response[0] - '0') * 100 + + (response[1] - '0') * 10 + + (response[2] - '0'); + } + + return code; +} + + +// static +const SMTPResponse::enhancedStatusCode SMTPResponse::extractEnhancedCode(const string& responseText) { + + enhancedStatusCode enhCode; + + std::istringstream iss(responseText); + iss.imbue(std::locale::classic()); + + if (std::isdigit(iss.peek())) { + + iss >> enhCode.klass; + + if (iss.get() == '.' && std::isdigit(iss.peek())) { + + iss >> enhCode.subject; + + if (iss.get() == '.' && std::isdigit(iss.peek())) { + + iss >> enhCode.detail; + return enhCode; + } + } + } + + return enhancedStatusCode(); // no enhanced code found +} + + +const SMTPResponse::responseLine SMTPResponse::getLineAt(const size_t pos) const { + + return m_lines[pos]; +} + + +size_t SMTPResponse::getLineCount() const { + + return m_lines.size(); +} + + +const SMTPResponse::responseLine SMTPResponse::getLastLine() const { + + return m_lines[m_lines.size() - 1]; +} + + +const SMTPResponse::state SMTPResponse::getCurrentState() const { + + state st; + st.responseBuffer = m_responseBuffer; + + return st; +} + + + +// SMTPResponse::responseLine + +SMTPResponse::responseLine::responseLine( + const int code, + const string& text, + const enhancedStatusCode& enhCode +) + : m_code(code), + m_text(text), + m_enhCode(enhCode) { + +} + + +void SMTPResponse::responseLine::setCode(const int code) { + + m_code = code; +} + + +int SMTPResponse::responseLine::getCode() const { + + return m_code; +} + + +void SMTPResponse::responseLine::setEnhancedCode(const enhancedStatusCode& enhCode) { + + m_enhCode = enhCode; +} + + +const SMTPResponse::enhancedStatusCode SMTPResponse::responseLine::getEnhancedCode() const { + + return m_enhCode; +} + + +void SMTPResponse::responseLine::setText(const string& text) { + + m_text = text; +} + + +const string SMTPResponse::responseLine::getText() const { + + return m_text; +} + + + +// SMTPResponse::enhancedStatusCode + + +SMTPResponse::enhancedStatusCode::enhancedStatusCode() + : klass(0), + subject(0), + detail(0) { + +} + + +SMTPResponse::enhancedStatusCode::enhancedStatusCode(const enhancedStatusCode& enhCode) + : klass(enhCode.klass), + subject(enhCode.subject), + detail(enhCode.detail) { + +} + + +std::ostream& operator<<(std::ostream& os, const SMTPResponse::enhancedStatusCode& code) { + + os << code.klass << '.' << code.subject << '.' << code.detail; + return os; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp b/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp new file mode 100644 index 0000000..2eec7f4 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPResponse.hpp @@ -0,0 +1,200 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" + + +namespace vmime { +namespace net { + + +class socket; +class timeoutHandler; +class tracer; + + +namespace smtp { + + +/** A SMTP response, as sent by the server. + */ +class VMIME_EXPORT SMTPResponse : public object { + +public: + + /** Current state of response parser. */ + struct state { + + string responseBuffer; + }; + + /** Enhanced status code (as per RFC-3463). */ + struct enhancedStatusCode { + + enhancedStatusCode(); + enhancedStatusCode(const enhancedStatusCode& enhCode); + + unsigned short klass; /**< Success/failure. */ + unsigned short subject; /**< Source of anomaly. */ + unsigned short detail; /**< Precise error condition. */ + }; + + /** An element of a SMTP response. */ + class responseLine { + + public: + + responseLine(const int code, const string& text, const enhancedStatusCode& enhCode); + + void setCode(const int code); + int getCode() const; + + void setEnhancedCode(const enhancedStatusCode& enhCode); + const enhancedStatusCode getEnhancedCode() const; + + void setText(const string& text); + const string getText() const; + + private: + + int m_code; + string m_text; + enhancedStatusCode m_enhCode; + }; + + /** Receive and parse a new SMTP response from the + * specified socket. + * + * @param tr tracer + * @param sok socket from which to read + * @param toh time-out handler + * @param st previous state of response parser for the specified socket + * @return SMTP response + * @throws exceptions::operation_timed_out if no data + * has been received within the granted time + */ + static shared_ptr <SMTPResponse> readResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st + ); + + /** Return the SMTP response code. + * + * @return response code + */ + int getCode() const; + + /** Return the SMTP enhanced status code, if available. + * + * @return enhanced status code + */ + const enhancedStatusCode getEnhancedCode() const; + + /** Return the SMTP response text. + * The text of each line is concatenated. + * + * @return response text + */ + const string getText() const; + + /** Return the response line at the specified position. + * + * @param pos line index + * @return line at the specified index + */ + const responseLine getLineAt(const size_t pos) const; + + /** Return the number of lines in the response. + * + * @return number of lines in the response + */ + size_t getLineCount() const; + + /** Return the last line in the response. + * + * @return last response line + */ + const responseLine getLastLine() const; + + /** Returns the current state of the response parser. + * + * @return current parser state + */ + const state getCurrentState() const; + +private: + + SMTPResponse( + const shared_ptr <tracer>& tr, + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const state& st + ); + + SMTPResponse(const SMTPResponse&); + + void readResponse(); + + const string readResponseLine(); + const responseLine getNextResponse(); + + static int extractResponseCode(const string& response); + static const enhancedStatusCode extractEnhancedCode(const string& responseText); + + + std::vector <responseLine> m_lines; + + shared_ptr <socket> m_socket; + shared_ptr <timeoutHandler> m_timeoutHandler; + shared_ptr <tracer> m_tracer; + + string m_responseBuffer; + bool m_responseContinues; +}; + + +VMIME_EXPORT std::ostream& operator<<(std::ostream& os, const SMTPResponse::enhancedStatusCode& code); + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPRESPONSE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp b/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp new file mode 100644 index 0000000..5158aa9 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPSTransport.cpp @@ -0,0 +1,82 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPSTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPSTransport::SMTPSTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth +) + : SMTPTransport(sess, auth, true) { + +} + + +SMTPSTransport::~SMTPSTransport() { + +} + + +const string SMTPSTransport::getProtocolName() const { + + return "smtps"; +} + + + +// Service infos + +SMTPServiceInfos SMTPSTransport::sm_infos(true); + + +const serviceInfos& SMTPSTransport::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& SMTPSTransport::getInfos() const { + + return sm_infos; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp b/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp new file mode 100644 index 0000000..f19b3c5 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPSTransport.hpp @@ -0,0 +1,74 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/smtp/SMTPTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** SMTPS transport service. + */ +class VMIME_EXPORT SMTPSTransport : public SMTPTransport { + +public: + + SMTPSTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth + ); + + ~SMTPSTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static SMTPServiceInfos sm_infos; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp new file mode 100644 index 0000000..a2ee3d5 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.cpp @@ -0,0 +1,144 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPServiceInfos::SMTPServiceInfos(const bool smtps) + : m_smtps(smtps) { + +} + + +const string SMTPServiceInfos::getPropertyPrefix() const { + + if (m_smtps) { + return "transport.smtps."; + } else { + return "transport.smtp."; + } +} + + +const SMTPServiceInfos::props& SMTPServiceInfos::getProperties() const { + + static props smtpProps = { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOLEAN, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + property("options.pipelining", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.chunking", serviceInfos::property::TYPE_BOOLEAN, "true"), + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "25"), + }; + + static props smtpsProps = { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOLEAN, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOLEAN, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + property("options.pipelining", serviceInfos::property::TYPE_BOOLEAN, "true"), + property("options.chunking", serviceInfos::property::TYPE_BOOLEAN, "true"), + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "465"), + }; + + return m_smtps ? smtpsProps : smtpProps; +} + + +const std::vector <serviceInfos::property> SMTPServiceInfos::getAvailableProperties() const { + + std::vector <property> list; + const props& p = getProperties(); + + // SMTP-specific options + list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH); +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_smtps) { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + + return list; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp new file mode 100644 index 0000000..005a0a2 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPServiceInfos.hpp @@ -0,0 +1,93 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** Information about SMTP service. + */ +class VMIME_EXPORT SMTPServiceInfos : public serviceInfos { + +public: + + SMTPServiceInfos(const bool smtps); + + struct props { + // SMTP-specific options + serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH; +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + serviceInfos::property PROPERTY_OPTIONS_PIPELINING; + serviceInfos::property PROPERTY_OPTIONS_CHUNKING; + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector <serviceInfos::property> getAvailableProperties() const; + +private: + + const bool m_smtps; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp b/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp new file mode 100644 index 0000000..561bd59 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPTransport.cpp @@ -0,0 +1,502 @@ +// +// 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_SMTP + + +#include "vmime/net/smtp/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPResponse.hpp" +#include "vmime/net/smtp/SMTPCommand.hpp" +#include "vmime/net/smtp/SMTPCommandSet.hpp" +#include "vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + +#include "vmime/exception.hpp" +#include "vmime/mailboxList.hpp" +#include "vmime/message.hpp" + +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/outputStreamSocketAdapter.hpp" +#include "vmime/utility/streamUtils.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPTransport::SMTPTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured +) + : transport(sess, getInfosInstance(), auth), + m_isSMTPS(secured), + m_needReset(false) { + +} + + +SMTPTransport::~SMTPTransport() { + + try { + + if (isConnected()) { + disconnect(); + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +const string SMTPTransport::getProtocolName() const { + + return "smtp"; +} + + +bool SMTPTransport::isSMTPS() const { + + return m_isSMTPS; +} + + +void SMTPTransport::connect() { + + if (isConnected()) { + throw exceptions::already_connected(); + } + + m_connection = make_shared <SMTPConnection>( + dynamicCast <SMTPTransport>(shared_from_this()), getAuthenticator() + ); + + m_connection->connect(); +} + + +bool SMTPTransport::isConnected() const { + + return m_connection && m_connection->isConnected(); +} + + +bool SMTPTransport::isSecuredConnection() const { + + if (!m_connection) { + return false; + } + + return m_connection->isSecuredConnection(); +} + + +shared_ptr <connectionInfos> SMTPTransport::getConnectionInfos() const { + + if (!m_connection) { + return null; + } + + return m_connection->getConnectionInfos(); +} + + +shared_ptr <SMTPConnection> SMTPTransport::getConnection() { + + return m_connection; +} + + +void SMTPTransport::disconnect() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + m_connection->disconnect(); + m_connection = null; +} + + +void SMTPTransport::noop() { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + m_connection->sendRequest(SMTPCommand::NOOP()); + + shared_ptr <SMTPResponse> resp = m_connection->readResponse(); + + if (resp->getCode() != 250) { + throw SMTPCommandError("NOOP", resp->getText(), resp->getCode(), resp->getEnhancedCode()); + } +} + + +// static +bool SMTPTransport::mailboxNeedsUTF8(const mailbox& mb) { + + bool all7bit = + utility::stringUtils::is7bit(mb.getEmail().getLocalName().getBuffer()) + && utility::stringUtils::is7bit(mb.getEmail().getDomainName().getBuffer()); + + for (size_t i = 0, n = mb.getName().getWordCount() ; all7bit && i != n ; ++i) { + all7bit = utility::stringUtils::is7bit(mb.getName().getWordAt(i)->getBuffer()); + } + + return !all7bit; +} + + +void SMTPTransport::sendEnvelope( + const mailbox& expeditor, + const mailboxList& recipients, + const mailbox& sender, + bool sendDATACommand, + const size_t size, + const dsnAttributes& dsnAttrs +) { + + // If no recipient/expeditor was found, throw an exception + if (recipients.isEmpty()) { + throw exceptions::no_recipient(); + } else if (expeditor.isEmpty()) { + throw exceptions::no_expeditor(); + } + + // If DSN extension is used, ensure it is supported by the server + if (!dsnAttrs.isEmpty() && !m_connection->hasExtension("DSN")) { + throw SMTPDSNExtensionNotSupportedException(); + } + + + const bool needReset = m_needReset; + const bool hasPipelining = m_connection->hasExtension("PIPELINING") && + getInfos().getPropertyValue <bool>(getSession(), + dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().PROPERTY_OPTIONS_PIPELINING); + + shared_ptr <SMTPResponse> resp; + shared_ptr <SMTPCommandSet> commands = SMTPCommandSet::create(hasPipelining); + + // Emit a "RSET" command if we previously sent a message on this connection + if (needReset) { + commands->addCommand(SMTPCommand::RSET()); + } + + // Check whether we need SMTPUTF8 + const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8"); + bool needSMTPUTF8 = false; + + if (!sender.isEmpty()) { + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(sender); + } else { + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(expeditor); + } + + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + + const mailbox& mbox = *recipients.getMailboxAt(i); + needSMTPUTF8 = needSMTPUTF8 || mailboxNeedsUTF8(mbox); + } + + // Emit the "MAIL" command + const bool hasSize = m_connection->hasExtension("SIZE"); + + if (!sender.isEmpty()) { + + commands->addCommand( + SMTPCommand::MAIL( + sender, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, + dsnAttrs.getNotificationConditions(), + dsnAttrs.getEnvelopId() + ) + ); + + } else { + + commands->addCommand( + SMTPCommand::MAIL( + expeditor, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, + dsnAttrs.getNotificationConditions(), + dsnAttrs.getEnvelopId() + ) + ); + } + + // Now, we will need to reset next time + m_needReset = true; + + // Emit a "RCPT TO" command for each recipient + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + + const mailbox& mbox = *recipients.getMailboxAt(i); + commands->addCommand(SMTPCommand::RCPT(mbox, hasSMTPUTF8 && needSMTPUTF8, + dsnAttrs.getNotificationConditions())); + } + + // Prepare sending of message data + if (sendDATACommand) { + commands->addCommand(SMTPCommand::DATA()); + } + + // Read response for "RSET" command + if (needReset) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250 && + resp->getCode() != 200) { // RFC-876: << In reply to a RSET and/or a NOOP command, + // some servers reply "200" >> + + disconnect(); + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + + // Read response for "MAIL" command + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + if ((resp = m_connection->readResponse())->getCode() != 250) { + + // SIZE extension: insufficient system storage + if (resp->getCode() == 452) { + + throw SMTPMessageSizeExceedsCurLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // SIZE extension: message size exceeds fixed maximum message size + } else if (resp->getCode() == 552) { + + throw SMTPMessageSizeExceedsMaxLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // Other error + } else { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + + // Read responses for "RCPT TO" commands + for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + resp = m_connection->readResponse(); + + if (resp->getCode() != 250 && + resp->getCode() != 251) { + + // SIZE extension: insufficient system storage + if (resp->getCode() == 452) { + + throw SMTPMessageSizeExceedsCurLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // SIZE extension: message size exceeds fixed maximum message size + } else if (resp->getCode() == 552) { + + throw SMTPMessageSizeExceedsMaxLimitsException( + SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ) + ); + + // Other error + } else { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } + } + + // Read response for "DATA" command + if (sendDATACommand) { + + commands->writeToSocket(m_connection->getSocket(), m_connection->getTracer()); + + if ((resp = m_connection->readResponse())->getCode() != 354) { + + throw SMTPCommandError( + commands->getLastCommandSent()->getText(), resp->getText(), + resp->getCode(), resp->getEnhancedCode() + ); + } + } +} + + +void SMTPTransport::send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& dsnAttrs +) { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + // Send message envelope + sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size, + dsnAttrs); + + // Send the message data + // Stream copy with "\n." to "\n.." transformation + utility::outputStreamSocketAdapter sos(*m_connection->getSocket()); + utility::dotFilteredOutputStream fos(sos); + + utility::bufferedStreamCopy(is, fos, size, progress); + + fos.flush(); + + // Send end-of-data delimiter + m_connection->getSocket()->send("\r\n.\r\n"); + + if (m_connection->getTracer()) { + m_connection->getTracer()->traceSendBytes(size); + m_connection->getTracer()->traceSend("."); + } + + shared_ptr <SMTPResponse> resp; + + if ((resp = m_connection->readResponse())->getCode() != 250) { + throw SMTPCommandError("DATA", resp->getText(), resp->getCode(), resp->getEnhancedCode()); + } +} + + +void SMTPTransport::send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& dsnAttrs +) { + + if (!isConnected()) { + throw exceptions::not_connected(); + } + + // Generate the message with Internationalized Email support, + // if this is supported by the SMTP server + generationContext ctx(generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(m_connection->hasExtension("SMTPUTF8")); + + // If CHUNKING is not supported, generate the message to a temporary + // buffer then use the send() method which takes an inputStream + if (!m_connection->hasExtension("CHUNKING") || + !getInfos().getPropertyValue <bool>(getSession(), + dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().PROPERTY_OPTIONS_CHUNKING)) { + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ctx, ossAdapter); + + const string& str(oss.str()); + + utility::inputStreamStringAdapter isAdapter(str); + + send(expeditor, recipients, isAdapter, str.length(), progress, sender, dsnAttrs); + return; + } + + // Send message envelope + const size_t msgSize = msg->getGeneratedSize(ctx); + + sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ false, msgSize, dsnAttrs); + + // Send the message by chunks + SMTPChunkingOutputStreamAdapter chunkStream(m_connection, msgSize, progress); + + msg->generate(ctx, chunkStream); + + chunkStream.flush(); +} + + + +// Service infos + +SMTPServiceInfos SMTPTransport::sm_infos(false); + + +const serviceInfos& SMTPTransport::getInfosInstance() { + + return sm_infos; +} + + +const serviceInfos& SMTPTransport::getInfos() const { + + return sm_infos; +} + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + diff --git a/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp b/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp new file mode 100644 index 0000000..cd7c712 --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/SMTPTransport.hpp @@ -0,0 +1,144 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + + +#include "vmime/net/transport.hpp" +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/net/smtp/SMTPServiceInfos.hpp" +#include "vmime/net/smtp/SMTPConnection.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +class SMTPCommand; + + +/** SMTP transport service. + */ +class VMIME_EXPORT SMTPTransport : public transport { + +public: + + SMTPTransport( + const shared_ptr <session>& sess, + const shared_ptr <security::authenticator>& auth, + const bool secured = false + ); + + ~SMTPTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + bool isConnected() const; + void disconnect(); + + void noop(); + + void send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + void send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + bool isSecuredConnection() const; + shared_ptr <connectionInfos> getConnectionInfos() const; + shared_ptr <SMTPConnection> getConnection(); + + bool isSMTPS() const; + +private: + + static bool mailboxNeedsUTF8(const mailbox& mb); + + /** Send the MAIL and RCPT commands to the server, checking the + * response, and using pipelining if supported by the server. + * Optionally, the DATA command can also be sent. + * + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param sender envelope sender (if empty, expeditor will be used) + * @param sendDATACommand if true, the DATA command will be sent + * @param size message size, in bytes (or 0, if not known) + * @param dsnAttributes attributes for Delivery Status Notification (if needed) + */ + void sendEnvelope( + const mailbox& expeditor, + const mailboxList& recipients, + const mailbox& sender, + bool sendDATACommand, + const size_t size, + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + + shared_ptr <SMTPConnection> m_connection; + + + const bool m_isSMTPS; + + bool m_needReset; + + // Service infos + static SMTPServiceInfos sm_infos; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP + +#endif // VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/smtp/smtp.hpp b/vmime-master/src/vmime/net/smtp/smtp.hpp new file mode 100644 index 0000000..4c0b17d --- /dev/null +++ b/vmime-master/src/vmime/net/smtp/smtp.hpp @@ -0,0 +1,33 @@ +// +// 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. +// + +#ifndef VMIME_NET_SMTP_SMTP_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTP_HPP_INCLUDED + + +#include "vmime/net/smtp/SMTPTransport.hpp" +#include "vmime/net/smtp/SMTPSTransport.hpp" +#include "vmime/net/smtp/SMTPExceptions.hpp" + + +#endif // VMIME_NET_SMTP_SMTP_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/socket.hpp b/vmime-master/src/vmime/net/socket.hpp new file mode 100644 index 0000000..a236a9f --- /dev/null +++ b/vmime-master/src/vmime/net/socket.hpp @@ -0,0 +1,223 @@ +// +// 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. +// + +#ifndef VMIME_NET_SOCKET_HPP_INCLUDED +#define VMIME_NET_SOCKET_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/base.hpp" + +#include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/tracer.hpp" + + +namespace vmime { +namespace net { + + +/** Interface for connecting to servers. + */ +class VMIME_EXPORT socket : public object { + +public: + + enum Status { + STATUS_WOULDBLOCK = 0xf, /**< The operation would block. Retry later. */ + STATUS_WANT_READ = 0x1, /**< The socket wants to read data, retry when data is available. */ + STATUS_WANT_WRITE = 0x2 /**< The socket wants to write data, retry when data can be written. */ + }; + + + virtual ~socket() { } + + + /** Connect to the specified address and port. + * + * @param address server address (this can be a full qualified domain name + * or an IP address, doesn't matter) + * @param port server port + */ + virtual void connect(const string& address, const port_t port) = 0; + + /** Disconnect from the server. + */ + virtual void disconnect() = 0; + + /** Test whether this socket is connected. + * + * @return true if the socket is connected, false otherwise + */ + virtual bool isConnected() const = 0; + + /** Block until new data is available for reading. The function will + * timeout after msecs milliseconds. + * + * @param timeout maximum wait time, in milliseconds (default is 30000); + * resolution is 10ms + * @return true if data is available, or false if the operation timed out + */ + virtual bool waitForRead(const int msecs = 30000) = 0; + + /** Block until pending data has been written and new data can be written. + * The function will timeout after msecs milliseconds. + * + * @param timeout maximum wait time, in milliseconds (default is 30000); + * resolution is 10ms + * @return true if new data can be written immediately, or false if the + * operation timed out + */ + virtual bool waitForWrite(const int msecs = 30000) = 0; + + /** Receive text data from the socket. + * + * @param buffer buffer in which to write received data + */ + virtual void receive(string& buffer) = 0; + + /** Receive raw data from the socket. + * + * @param buffer buffer in which to write received data + * @param count maximum number of bytes to receive (size of buffer) + * @return number of bytes received/written into output buffer + */ + virtual size_t receiveRaw(byte_t* buffer, const size_t count) = 0; + + /** Send text data to the socket. + * + * @param buffer data to send + */ + virtual void send(const string& buffer) = 0; + + /** Send text data to the socket. + * + * @param str null-terminated string + */ + virtual void send(const char* str) = 0; + + /** Send raw data to the socket. + * + * @param buffer data to send + * @param count number of bytes to send (size of buffer) + */ + virtual void sendRaw(const byte_t* buffer, const size_t count) = 0; + + /** Send raw data to the socket. + * Function may returns before all data is sent. + * + * @param buffer data to send + * @param count number of bytes to send (size of buffer) + * @return number of bytes sent + */ + virtual size_t sendRawNonBlocking(const byte_t* buffer, const size_t count) = 0; + + /** Return the preferred maximum block size when reading + * from or writing to this stream. + * + * @return block size, in bytes + */ + virtual size_t getBlockSize() const = 0; + + /** Return the current status of this socket. + * + * @return status flags for this socket + */ + virtual unsigned int getStatus() const = 0; + + /** Return the hostname of peer this socket is connected to. + * + * @return name of the peer, or numeric address if it cannot be found + */ + virtual const string getPeerName() const = 0; + + /** Return the address of peer this socket is connected to. + * + * @return numeric address of the peer + */ + virtual const string getPeerAddress() const = 0; + + /** Return the timeout handler associated with this socket. + * + * @return timeout handler, or NULL if none is set + */ + virtual shared_ptr <timeoutHandler> getTimeoutHandler() = 0; + + /** Set the tracer used by this socket. Tracer will only be used + * to report socket-specific events such as connection (not when + * sending/receiving data). + * + * @param tracer tracer to use + */ + virtual void setTracer(const shared_ptr <tracer>& tracer) = 0; + + /** Return the tracer used by this socket. + * + * @return tracer, or NULL if none is set + */ + virtual shared_ptr <tracer> getTracer() = 0; + +protected: + + socket() { } + +private: + + socket(const socket&) : object() { } +}; + + +/** A class to create 'socket' objects. + */ +class socketFactory : public object { + +public: + + virtual ~socketFactory() { } + + /** Creates a socket without timeout handler. + * + * @return a new socket + */ + virtual shared_ptr <socket> create() = 0; + + /** Creates a socket with the specified timeout handler. + * + * @param th timeout handler + * @return a new socket + */ + virtual shared_ptr <socket> create(const shared_ptr <timeoutHandler>& th) = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_SOCKET_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/store.cpp b/vmime-master/src/vmime/net/store.cpp new file mode 100644 index 0000000..3c9a66d --- /dev/null +++ b/vmime-master/src/vmime/net/store.cpp @@ -0,0 +1,57 @@ +// +// 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 + + +#include "vmime/net/store.hpp" + + +namespace vmime { +namespace net { + + +store::store( + const shared_ptr <session>& sess, + const serviceInfos& infos, + const shared_ptr <security::authenticator>& auth +) + : service(sess, infos, auth) { + +} + + +store::Type store::getType() const { + + return TYPE_STORE; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/store.hpp b/vmime-master/src/vmime/net/store.hpp new file mode 100644 index 0000000..a96f267 --- /dev/null +++ b/vmime-master/src/vmime/net/store.hpp @@ -0,0 +1,115 @@ +// +// 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. +// + +#ifndef VMIME_NET_STORE_HPP_INCLUDED +#define VMIME_NET_STORE_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/service.hpp" +#include "vmime/net/folder.hpp" + + +namespace vmime { +namespace net { + + +/** A store service. + * Encapsulate protocols that provide access to user's mail drop. + */ +class VMIME_EXPORT store : public service { + +protected: + + store( + const shared_ptr <session>& sess, + const serviceInfos& infos, + const shared_ptr <security::authenticator>& auth + ); + +public: + + /** Return the default folder. This is protocol dependent + * and usually is the INBOX folder. + * + * @return default folder + */ + virtual shared_ptr <folder> getDefaultFolder() = 0; + + /** Return the root folder. This is protocol dependent + * and usually is the user's mail drop root folder. + * + * @return root folder + */ + virtual shared_ptr <folder> getRootFolder() = 0; + + /** Return the folder specified by the path. + * + * @param path absolute folder path + * @return folder at the specified path + */ + virtual shared_ptr <folder> getFolder(const folder::path& path) = 0; + + /** Test whether the specified folder name is a syntactically + * a valid name. + * + * @return true if the specified folder name is valid, false otherwise + */ + virtual bool isValidFolderName(const folder::path::component& name) const = 0; + + /** Store capabilities. */ + enum Capabilities { + CAPABILITY_CREATE_FOLDER = (1 << 0), /**< Can create folders. */ + CAPABILITY_RENAME_FOLDER = (1 << 1), /**< Can rename folders. */ + CAPABILITY_ADD_MESSAGE = (1 << 2), /**< Can append message to folders. */ + CAPABILITY_COPY_MESSAGE = (1 << 3), /**< Can copy messages from a folder to another one. */ + CAPABILITY_DELETE_MESSAGE = (1 << 4), /**< Can delete messages. */ + CAPABILITY_PARTIAL_FETCH = (1 << 5), /**< Is partial fetch supported? */ + CAPABILITY_MESSAGE_FLAGS = (1 << 6), /**< Can set flags on messages. */ + CAPABILITY_EXTRACT_PART = (1 << 7) /**< Can extract a specific part of the message. */ + }; + + /** Return the features supported by this service. This is + * a combination of store::CAPABILITY_xxx flags. + * + * @return features supported by this service + */ + virtual int getCapabilities() const = 0; + + + Type getType() const; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_STORE_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/timeoutHandler.hpp b/vmime-master/src/vmime/net/timeoutHandler.hpp new file mode 100644 index 0000000..fed1bc4 --- /dev/null +++ b/vmime-master/src/vmime/net/timeoutHandler.hpp @@ -0,0 +1,89 @@ +// +// 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. +// + +#ifndef VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED +#define VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { + + +/** A class to manage timeouts in messaging services. This can be used + * to stop operations that takes too much time to complete (ie. no data + * received from the server for a long time if the network link is down). + */ +class VMIME_EXPORT timeoutHandler : public object { + +public: + + virtual ~timeoutHandler() { } + + /** Called to test if the time limit has been reached. + * + * @return true if the timeout delay is elapsed + */ + virtual bool isTimeOut() = 0; + + /** Called to reset the timeout counter. + */ + virtual void resetTimeOut() = 0; + + /** Called when the time limit has been reached (when + * isTimeOut() returned true). + * + * @return true to continue (and reset the timeout) + * or false to cancel the current operation + */ + virtual bool handleTimeOut() = 0; +}; + + +/** A class to create 'timeoutHandler' objects. + */ +class timeoutHandlerFactory : public object { + +public: + + virtual ~timeoutHandlerFactory() { } + + virtual shared_ptr <timeoutHandler> create() = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/TLSProperties.cpp b/vmime-master/src/vmime/net/tls/TLSProperties.cpp new file mode 100644 index 0000000..f7721d4 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSProperties.cpp @@ -0,0 +1,44 @@ +// +// 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_TLS_SUPPORT + + +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + diff --git a/vmime-master/src/vmime/net/tls/TLSProperties.hpp b/vmime-master/src/vmime/net/tls/TLSProperties.hpp new file mode 100644 index 0000000..94341ca --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSProperties.hpp @@ -0,0 +1,105 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSPROPERTIES_HPP_INCLUDED +#define VMIME_NET_TLS_TLSPROPERTIES_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Holds options for a TLS session. + */ +class VMIME_EXPORT TLSProperties : public object { + +public: + + TLSProperties(); + TLSProperties(const TLSProperties&); + + + /** Predefined generic cipher suites (work with all TLS libraries). */ + enum GenericCipherSuite { + + CIPHERSUITE_HIGH, /**< High encryption cipher suites (> 128 bits). */ + CIPHERSUITE_MEDIUM, /**< Medium encryption cipher suites (>= 128 bits). */ + CIPHERSUITE_LOW, /**< Low encryption cipher suites (>= 64 bits). */ + + CIPHERSUITE_DEFAULT /**< Default cipher suite. */ + }; + + /** Sets the cipher suite preferences for a SSL/TLS session, using + * predefined, generic suites. This works with all underlying TLS + * libraries (OpenSSL and GNU TLS). + * + * @param cipherSuite predefined cipher suite + */ + void setCipherSuite(const GenericCipherSuite cipherSuite); + + /** Sets the cipher suite preferences for a SSL/TLS session, using + * a character string. The format and meaning of the string depend + * on the underlying TLS library. + * + * For GNU TLS, read this: + * http://gnutls.org/manual/html_node/Priority-Strings.html + * + * For OpenSSL, read this: + * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS + * + * @param cipherSuite cipher suite as a string + */ + void setCipherSuite(const string& cipherSuite); + + /** Returns the cipher suite preferences for a SSL/TLS session, as + * a character string. The format and meaning of the string depend + * on the underlying TLS library (see setCipherSuite() method). + * + * @return cipher suite string + */ + const string getCipherSuite() const; + +private: + + shared_ptr <object> m_data; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + +#endif // VMIME_NET_TLS_TLSPROPERTIES_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp b/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp new file mode 100644 index 0000000..055dfea --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp @@ -0,0 +1,77 @@ +// +// 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_TLS_SUPPORT + + +#include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" +#include "vmime/net/tls/TLSSession.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +TLSSecuredConnectionInfos::TLSSecuredConnectionInfos( + const string& host, + const port_t port, + const shared_ptr <TLSSession>& tlsSession, + const shared_ptr <TLSSocket>& tlsSocket +) + : m_host(host), + m_port(port), + m_tlsSession(tlsSession), + m_tlsSocket(tlsSocket) { + +} + + +const string TLSSecuredConnectionInfos::getHost() const { + + return m_host; +} + + +port_t TLSSecuredConnectionInfos::getPort() const { + + return m_port; +} + + +shared_ptr <const security::cert::certificateChain> TLSSecuredConnectionInfos::getPeerCertificates() const { + + return m_tlsSocket->getPeerCertificates(); +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + diff --git a/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp b/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp new file mode 100644 index 0000000..c65e9d2 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp @@ -0,0 +1,88 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLSSECUREDCONNECTIONINFOS_HPP_INCLUDED +#define VMIME_NET_TLSSECUREDCONNECTIONINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + + +#include "vmime/net/securedConnectionInfos.hpp" + +#include "vmime/security/cert/certificateChain.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSocket; + + +/** Information about a TLS-secured connection used by a service. + */ +class VMIME_EXPORT TLSSecuredConnectionInfos : public securedConnectionInfos { + +public: + + TLSSecuredConnectionInfos( + const string& host, + const port_t port, + const shared_ptr <TLSSession>& tlsSession, + const shared_ptr <TLSSocket>& tlsSocket + ); + + const string getHost() const; + port_t getPort() const; + + /** Return the peer's certificate (chain) as sent by the peer. + * + * @return server certificate chain + */ + shared_ptr <const security::cert::certificateChain> getPeerCertificates() const; + +private: + + string m_host; + port_t m_port; + + shared_ptr <TLSSession> m_tlsSession; + shared_ptr <TLSSocket> m_tlsSocket; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + +#endif // VMIME_NET_TLSSECUREDCONNECTIONINFOS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/TLSSession.cpp b/vmime-master/src/vmime/net/tls/TLSSession.cpp new file mode 100644 index 0000000..ab8b7c3 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSession.cpp @@ -0,0 +1,48 @@ +// +// 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_TLS_SUPPORT + + +#include "vmime/net/tls/TLSSession.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +TLSSession::TLSSession() { + +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT diff --git a/vmime-master/src/vmime/net/tls/TLSSession.hpp b/vmime-master/src/vmime/net/tls/TLSSession.hpp new file mode 100644 index 0000000..9e84fe7 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSession.hpp @@ -0,0 +1,96 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSProperties.hpp" + +#include "vmime/security/cert/certificateVerifier.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Describe a TLS connection between a client and a server. + */ +class VMIME_EXPORT TLSSession : public object, public enable_shared_from_this <TLSSession> { + +public: + + /** Create and initialize a new TLS session. + * + * @param cv object responsible for verifying certificates + * sent by the server + * @param props TLS properties for this session + * @return a new TLS session + */ + static shared_ptr <TLSSession> create( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props + ); + + /** Create a new socket that adds a TLS security layer around + * an existing socket. You should create only one socket + * per session. + * + * @param sok socket to wrap + * @return TLS socket wrapper + */ + virtual shared_ptr <TLSSocket> getSocket(const shared_ptr <socket>& sok) = 0; + + /** Get the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + virtual shared_ptr <security::cert::certificateVerifier> getCertificateVerifier() = 0; + +protected: + + TLSSession(); + +private: + + TLSSession(const TLSSession&); +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + +#endif // VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/TLSSocket.cpp b/vmime-master/src/vmime/net/tls/TLSSocket.cpp new file mode 100644 index 0000000..fbca082 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSocket.cpp @@ -0,0 +1,44 @@ +// +// 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_TLS_SUPPORT + + +#include "vmime/net/tls/TLSSocket.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + diff --git a/vmime-master/src/vmime/net/tls/TLSSocket.hpp b/vmime-master/src/vmime/net/tls/TLSSocket.hpp new file mode 100644 index 0000000..ca50aa8 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/TLSSocket.hpp @@ -0,0 +1,88 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + + +#include "vmime/exception.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/security/cert/certificateChain.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; + + +/** Add a TLS security layer to an existing socket. + */ +class VMIME_EXPORT TLSSocket : public socket { + +public: + + /** Create a new socket object that adds a security layer + * around an existing socket. + * + * @param session TLS session + * @param sok socket to wrap + */ + static shared_ptr <TLSSocket> wrap(const shared_ptr <TLSSession>& session, const shared_ptr <socket>& sok); + + /** Starts a TLS handshake on this connection. + * + * @throw exceptions::tls_exception if a fatal error occurs + * during the negociation process, exceptions::operation_timed_out + * if a time-out occurs + */ + virtual void handshake() = 0; + + /** Return the peer's certificate (chain) as sent by the peer. + * + * @return server certificate chain, or NULL if the handshake + * has not been performed yet + */ + virtual shared_ptr <security::cert::certificateChain> getPeerCertificates() = 0; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + +#endif // VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp new file mode 100644 index 0000000..b2996fb --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp @@ -0,0 +1,113 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/base.hpp" +#include "vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp" + +#include <gnutls/gnutls.h> +#if GNUTLS_VERSION_NUMBER < 0x030000 +#include <gnutls/extra.h> +#endif + + +namespace vmime { +namespace net { +namespace tls { + + +TLSProperties::TLSProperties() + : m_data(make_shared <TLSProperties_GnuTLS>()) { + + setCipherSuite(CIPHERSUITE_DEFAULT); +} + + +TLSProperties::TLSProperties(const TLSProperties& props) + : object(), + m_data(make_shared <TLSProperties_GnuTLS>()) { + + *dynamicCast <TLSProperties_GnuTLS>(m_data) = *dynamicCast <TLSProperties_GnuTLS>(props.m_data); +} + + +void TLSProperties::setCipherSuite(const GenericCipherSuite cipherSuite) { + + switch (cipherSuite) { + + case CIPHERSUITE_HIGH: + + setCipherSuite("SECURE256:%SSL3_RECORD_VERSION"); + break; + + case CIPHERSUITE_MEDIUM: + + setCipherSuite("SECURE128:%SSL3_RECORD_VERSION"); + break; + + case CIPHERSUITE_LOW: + + setCipherSuite("NORMAL:%SSL3_RECORD_VERSION"); + break; + + default: + case CIPHERSUITE_DEFAULT: + + setCipherSuite("NORMAL:%SSL3_RECORD_VERSION"); + break; + } +} + + +void TLSProperties::setCipherSuite(const string& cipherSuite) { + + dynamicCast <TLSProperties_GnuTLS>(m_data)->cipherSuite = cipherSuite; +} + + +const string TLSProperties::getCipherSuite() const { + + return dynamicCast <TLSProperties_GnuTLS>(m_data)->cipherSuite; +} + + + +TLSProperties_GnuTLS& TLSProperties_GnuTLS::operator=(const TLSProperties_GnuTLS& other) { + + cipherSuite = other.cipherSuite; + + return *this; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp new file mode 100644 index 0000000..96bbaea --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp @@ -0,0 +1,68 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSPROPERTIES_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSPROPERTIES_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSProperties_GnuTLS : public object { + +public: + + TLSProperties_GnuTLS& operator=(const TLSProperties_GnuTLS& other); + + + string cipherSuite; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSPROPERTIES_GNUTLS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp new file mode 100644 index 0000000..8586537 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp @@ -0,0 +1,313 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include <gnutls/gnutls.h> +#if GNUTLS_VERSION_NUMBER < 0x030000 +#include <gnutls/extra.h> +#endif + + +// Dependency on gcrypt is not needed since GNU TLS version 2.12. +// See here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=638651 +#if GNUTLS_VERSION_NUMBER <= 0x020b00 +# define VMIME_GNUTLS_NEEDS_GCRYPT 1 +#endif + +#if VMIME_HAVE_PTHREAD +# include <pthread.h> +# if VMIME_GNUTLS_NEEDS_GCRYPT +# include <gcrypt.h> +# endif +# include <errno.h> +#endif // VMIME_HAVE_PTHREAD + +#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp" + +#include "vmime/exception.hpp" + + +// Enable GnuTLS debugging by defining GNUTLS_DEBUG +//#define GNUTLS_DEBUG 1 + + +#include <sstream> +#include <iomanip> + +#if VMIME_DEBUG && GNUTLS_DEBUG + #include <iostream> +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + +#if VMIME_HAVE_PTHREAD && VMIME_GNUTLS_NEEDS_GCRYPT && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) +extern "C" +{ + GCRY_THREAD_OPTION_PTHREAD_IMPL; +} +#endif // VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL + + +namespace vmime { +namespace net { +namespace tls { + + +#ifndef VMIME_BUILDING_DOC + +// Initialize GNU TLS library +struct TLSGlobal { + + TLSGlobal() { + +#if VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) + #if VMIME_GNUTLS_NEEDS_GCRYPT + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + #endif // VMIME_GNUTLS_NEEDS_GCRYPT +#endif // VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL + + gnutls_global_init(); + //gnutls_global_init_extra(); + +#if VMIME_DEBUG && GNUTLS_DEBUG + gnutls_global_set_log_function(TLSLogFunc); + gnutls_global_set_log_level(10); +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + gnutls_anon_allocate_client_credentials(&anonCred); + gnutls_certificate_allocate_credentials(&certCred); + } + + ~TLSGlobal() { + + gnutls_anon_free_client_credentials(anonCred); + gnutls_certificate_free_credentials(certCred); + + gnutls_global_deinit(); + } + +#if VMIME_DEBUG && GNUTLS_DEBUG + + static void TLSLogFunc(int level, const char *str) { + + std::cerr << "GNUTLS: [" << level << "] " << str << std::endl; + } + +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + + gnutls_anon_client_credentials_t anonCred; + gnutls_certificate_credentials_t certCred; +}; + +static TLSGlobal g_gnutlsGlobal; + + +#endif // VMIME_BUILDING_DOC + + + +// static +shared_ptr <TLSSession> TLSSession::create( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) { + + return make_shared <TLSSession_GnuTLS>(cv, props); +} + + +TLSSession_GnuTLS::TLSSession_GnuTLS( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) + : m_certVerifier(cv), + m_props(props) { + + int res; + + m_gnutlsSession = new gnutls_session_t; + + if (gnutls_init(m_gnutlsSession, GNUTLS_CLIENT) != 0) { + throw std::bad_alloc(); + } + + // Sets some default priority on the ciphers, key exchange methods, + // macs and compression methods. +#ifdef VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + gnutls_dh_set_prime_bits(*m_gnutlsSession, 128); + + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, m_props->getCipherSuite().c_str(), NULL)) != 0) { + + throwTLSException("gnutls_priority_set_direct", res); + } + +#else // !VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + + gnutls_set_default_priority(*m_gnutlsSession); + + // Sets the priority on the certificate types supported by gnutls. + // Priority is higher for types specified before others. After + // specifying the types you want, you must append a 0. + const int certTypePriority[] = { GNUTLS_CRT_X509, 0 }; + + res = gnutls_certificate_type_set_priority(*m_gnutlsSession, certTypePriority); + + if (res < 0) { + throwTLSException("gnutls_certificate_type_set_priority", res); + } + + // Sets the priority on the protocol types + const int protoPriority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; + + res = gnutls_protocol_set_priority(*m_gnutlsSession, protoPriority); + + if (res < 0) { + throwTLSException("gnutls_certificate_type_set_priority", res); + } + + // Priority on the ciphers + const int cipherPriority[] = { + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_AES_256_CBC, + GNUTLS_CIPHER_ARCFOUR_40, + GNUTLS_CIPHER_RC2_40_CBC, + GNUTLS_CIPHER_DES_CBC, + 0 + }; + + gnutls_cipher_set_priority(*m_gnutlsSession, cipherPriority); + + // Priority on MACs + const int macPriority[] = { + GNUTLS_MAC_SHA, + GNUTLS_MAC_MD5, + 0 + }; + + gnutls_mac_set_priority(*m_gnutlsSession, macPriority); + + // Priority on key exchange methods + const int kxPriority[] = { + GNUTLS_KX_RSA, + GNUTLS_KX_DHE_DSS, + GNUTLS_KX_DHE_RSA, + GNUTLS_KX_ANON_DH, + GNUTLS_KX_SRP, + GNUTLS_KX_RSA_EXPORT, + GNUTLS_KX_SRP_RSA, + GNUTLS_KX_SRP_DSS, + 0 + }; + + gnutls_kx_set_priority(*m_gnutlsSession, kxPriority); + + // Priority on compression methods + const int compressionPriority[] = { + GNUTLS_COMP_ZLIB, + //GNUTLS_COMP_LZO, + GNUTLS_COMP_NULL, + 0 + }; + + gnutls_compression_set_priority(*m_gnutlsSession, compressionPriority); + +#endif // !VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + + // Initialize credentials + gnutls_credentials_set( + *m_gnutlsSession, GNUTLS_CRD_ANON, g_gnutlsGlobal.anonCred + ); + + gnutls_credentials_set( + *m_gnutlsSession, GNUTLS_CRD_CERTIFICATE, g_gnutlsGlobal.certCred + ); +} + + +TLSSession_GnuTLS::TLSSession_GnuTLS(const TLSSession_GnuTLS&) + : TLSSession() { + + // Not used +} + + +TLSSession_GnuTLS::~TLSSession_GnuTLS() { + + try { + + if (m_gnutlsSession) { + + gnutls_deinit(*m_gnutlsSession); + + delete m_gnutlsSession; + m_gnutlsSession = NULL; + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +shared_ptr <TLSSocket> TLSSession_GnuTLS::getSocket(const shared_ptr <socket>& sok) { + + return TLSSocket::wrap(dynamicCast <TLSSession>(shared_from_this()), sok); +} + + +shared_ptr <security::cert::certificateVerifier> TLSSession_GnuTLS::getCertificateVerifier() { + + return m_certVerifier; +} + + +void TLSSession_GnuTLS::throwTLSException(const string& fname, const int code) { + + std::ostringstream msg; + + msg << fname + "() returned code "; + msg << std::hex << code; + msg << ": "; + msg << gnutls_strerror(code); + + throw exceptions::tls_exception(msg.str()); +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp new file mode 100644 index 0000000..2a7f9d7 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp @@ -0,0 +1,95 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSESSION_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSession.hpp" +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_GnuTLS : public TLSSession { + + friend class TLSSocket_GnuTLS; + +public: + + TLSSession_GnuTLS( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props + ); + + ~TLSSession_GnuTLS(); + + + shared_ptr <TLSSocket> getSocket(const shared_ptr <socket>& sok); + + shared_ptr <security::cert::certificateVerifier> getCertificateVerifier(); + +private: + + TLSSession_GnuTLS(const TLSSession_GnuTLS&); + + static void throwTLSException(const string& fname, const int code); + + +#ifdef LIBGNUTLS_VERSION + gnutls_session_t* m_gnutlsSession; +#else + void* m_gnutlsSession; +#endif // LIBGNUTLS_VERSION + + shared_ptr <security::cert::certificateVerifier> m_certVerifier; + shared_ptr <TLSProperties> m_props; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSESSION_GNUTLS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp new file mode 100644 index 0000000..53e4eae --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -0,0 +1,548 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <errno.h> + +#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/security/cert/X509Certificate.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include <cstring> + + +namespace vmime { +namespace net { +namespace tls { + + +// static +shared_ptr <TLSSocket> TLSSocket::wrap( + const shared_ptr <TLSSession>& session, + const shared_ptr <socket>& sok +) +{ + return make_shared <TLSSocket_GnuTLS>(dynamicCast <TLSSession_GnuTLS>(session), sok); +} + + +TLSSocket_GnuTLS::TLSSocket_GnuTLS( + const shared_ptr <TLSSession_GnuTLS>& session, + const shared_ptr <socket>& sok +) + : m_session(session), + m_wrapped(sok), + m_connected(false), + m_ex(NULL), + m_status(0), + m_errno(0) { + + gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this); + + gnutls_transport_set_push_function(*m_session->m_gnutlsSession, gnutlsPushFunc); + gnutls_transport_set_pull_function(*m_session->m_gnutlsSession, gnutlsPullFunc); + gnutls_transport_set_errno_function(*m_session->m_gnutlsSession, gnutlsErrnoFunc); +} + + +TLSSocket_GnuTLS::~TLSSocket_GnuTLS() { + + resetException(); + + try { + disconnect(); + } catch (...) { + // Don't throw exception in destructor + } +} + + +void TLSSocket_GnuTLS::connect(const string& address, const port_t port) { + + try { + + m_wrapped->connect(address, port); + + handshake(); + + } catch (...) { + + disconnect(); + throw; + } +} + + +void TLSSocket_GnuTLS::disconnect() { + + if (m_connected) { + + gnutls_bye(*m_session->m_gnutlsSession, GNUTLS_SHUT_RDWR); + + m_wrapped->disconnect(); + + m_connected = false; + } +} + + +bool TLSSocket_GnuTLS::isConnected() const { + + return m_wrapped->isConnected() && m_connected; +} + + +size_t TLSSocket_GnuTLS::getBlockSize() const { + + return 16384; // 16 KB +} + + +const string TLSSocket_GnuTLS::getPeerName() const { + + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_GnuTLS::getPeerAddress() const { + + return m_wrapped->getPeerAddress(); +} + + +shared_ptr <timeoutHandler> TLSSocket_GnuTLS::getTimeoutHandler() { + + return m_wrapped->getTimeoutHandler(); +} + + +void TLSSocket_GnuTLS::setTracer(const shared_ptr <net::tracer>& tracer) { + + m_wrapped->setTracer(tracer); +} + + +shared_ptr <net::tracer> TLSSocket_GnuTLS::getTracer() { + + return m_wrapped->getTracer(); +} + + +bool TLSSocket_GnuTLS::waitForRead(const int msecs) { + + return m_wrapped->waitForRead(msecs); +} + + +bool TLSSocket_GnuTLS::waitForWrite(const int msecs) { + + return m_wrapped->waitForWrite(msecs); +} + + +void TLSSocket_GnuTLS::receive(string& buffer) { + + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); +} + + +void TLSSocket_GnuTLS::send(const string& buffer) { + + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); +} + + +void TLSSocket_GnuTLS::send(const char* str) { + + sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str)); +} + + +size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + resetException(); + + const ssize_t ret = gnutls_record_recv( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(count) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_status |= STATUS_WANT_READ; + } else { + m_status |= STATUS_WANT_WRITE; + } + + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_recv", static_cast <int>(ret)); + } + + return static_cast <size_t>(ret); +} + + +void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + for (size_t size = count ; size > 0 ; ) { + + resetException(); + + ssize_t ret = gnutls_record_send( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(size) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_wrapped->waitForRead(); + } else { + m_wrapped->waitForWrite(); + } + + continue; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret)); + + } else { + + buffer += ret; + size -= ret; + } + } +} + + +size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + resetException(); + + ssize_t ret = gnutls_record_send( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(count) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_status |= STATUS_WANT_READ; + } else { + m_status |= STATUS_WANT_WRITE; + } + + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret)); + } + + return static_cast <size_t>(ret); +} + + +unsigned int TLSSocket_GnuTLS::getStatus() const { + + return m_status | m_wrapped->getStatus(); +} + + +void TLSSocket_GnuTLS::handshake() { + + shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); + + if (toHandler) { + toHandler->resetTimeOut(); + } + + if (getTracer()) { + getTracer()->traceSend("Beginning SSL/TLS handshake"); + } + + // Start handshaking process + try { + string peerName = getPeerName(); + + gnutls_server_name_set(*m_session->m_gnutlsSession, GNUTLS_NAME_DNS, peerName.c_str(), peerName.size()); + + while (true) { + + resetException(); + + const int ret = gnutls_handshake(*m_session->m_gnutlsSession); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_wrapped->waitForRead(); + } else { + m_wrapped->waitForWrite(); + } + + } else if (ret == GNUTLS_E_INTERRUPTED) { + + // Non-fatal error + + } else { + + TLSSession_GnuTLS::throwTLSException("gnutls_handshake", ret); + } + + } else { + + // Successful handshake + break; + } + } + + } catch (...) { + + throw; + } + + // Verify server's certificate(s) + shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); + + if (certs == NULL) { + throw exceptions::tls_exception("No peer certificate."); + } + + m_session->getCertificateVerifier()->verify(certs, getPeerName()); + + m_connected = true; +} + + +int TLSSocket_GnuTLS::gnutlsErrnoFunc(gnutls_transport_ptr_t trspt) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + return sok->m_errno; +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPushFunc( + gnutls_transport_ptr_t trspt, + const void* data, + size_t len +) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + + try { + + const ssize_t ret = static_cast <ssize_t>( + sok->m_wrapped->sendRawNonBlocking(reinterpret_cast <const byte_t*>(data), len) + ); + + if (ret == 0) { + + gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN); + sok->m_errno = EAGAIN; + return -1; + } + + return ret; + + } catch (exception& e) { + + // Workaround for non-portable behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPullFunc( + gnutls_transport_ptr_t trspt, + void* data, + size_t len +) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + + try { + + const ssize_t n = static_cast <ssize_t>( + sok->m_wrapped->receiveRaw(reinterpret_cast <byte_t*>(data), len) + ); + + if (n == 0) { + + gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN); + sok->m_errno = EAGAIN; + return -1; + } + + return n; + + } catch (exception& e) { + + // Workaround for non-portable behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } +} + + +shared_ptr <security::cert::certificateChain> TLSSocket_GnuTLS::getPeerCertificates() { + + if (getTracer()) { + getTracer()->traceSend("Getting peer certificates"); + } + + unsigned int certCount = 0; + const gnutls_datum_t* rawData = gnutls_certificate_get_peers( + *m_session->m_gnutlsSession, &certCount + ); + + if (rawData == NULL) { + return null; + } + + // Try X.509 + gnutls_x509_crt_t* x509Certs = new gnutls_x509_crt_t[certCount]; + + for (unsigned int i = 0; i < certCount; ++i) { + + gnutls_x509_crt_init(x509Certs + i); + + int res = gnutls_x509_crt_import(x509Certs[i], rawData + i, GNUTLS_X509_FMT_DER); + + if (res < 0) { + + for (unsigned int j = 0 ; j <= i ; ++j) { + gnutls_x509_crt_deinit(x509Certs[j]); + } + + // XXX more fine-grained error reporting? + delete [] x509Certs; + return null; + } + } + + std::vector <shared_ptr <security::cert::certificate> > certs; + bool error = false; + + for (unsigned int i = 0 ; i < certCount ; ++i) { + + size_t dataSize = 0; + + gnutls_x509_crt_export(x509Certs[i], GNUTLS_X509_FMT_DER, NULL, &dataSize); + + std::vector <byte_t> data(dataSize); + + gnutls_x509_crt_export(x509Certs[i], GNUTLS_X509_FMT_DER, &data[0], &dataSize); + + shared_ptr <security::cert::X509Certificate> cert = + security::cert::X509Certificate::import(&data[0], dataSize); + + if (cert != NULL) { + certs.push_back(cert); + } else { + error = true; + } + + gnutls_x509_crt_deinit(x509Certs[i]); + } + + delete [] x509Certs; + + if (error) { + return null; + } + + return make_shared <security::cert::certificateChain>(certs); +} + + +// Following is a workaround for C++ exceptions to pass correctly between +// C and C++ calls. +// +// gnutls_record_recv() calls TLSSocket::gnutlsPullFunc, and exceptions +// thrown by the socket can not be caught. + +void TLSSocket_GnuTLS::throwException() { + + if (m_ex) { + throw *m_ex; + } +} + + +void TLSSocket_GnuTLS::resetException() { + + if (m_ex) { + delete m_ex; + m_ex = NULL; + } +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp new file mode 100644 index 0000000..0ac3e70 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -0,0 +1,129 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSOCKET_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/net/tls/TLSSocket.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSession_GnuTLS; + + +class TLSSocket_GnuTLS : public TLSSocket { + +public: + + TLSSocket_GnuTLS(const shared_ptr <TLSSession_GnuTLS>& session, const shared_ptr <socket>& sok); + ~TLSSocket_GnuTLS(); + + + void handshake(); + + shared_ptr <security::cert::certificateChain> getPeerCertificates(); + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + bool isConnected() const; + + bool waitForRead(const int msecs = 30000); + bool waitForWrite(const int msecs = 30000); + + void receive(string& buffer); + size_t receiveRaw(byte_t* buffer, const size_t count); + + void send(const string& buffer); + void send(const char* str); + void sendRaw(const byte_t* buffer, const size_t count); + size_t sendRawNonBlocking(const byte_t* buffer, const size_t count); + + size_t getBlockSize() const; + + unsigned int getStatus() const; + + const string getPeerName() const; + const string getPeerAddress() const; + + shared_ptr <timeoutHandler> getTimeoutHandler(); + + void setTracer(const shared_ptr <net::tracer>& tracer); + shared_ptr <net::tracer> getTracer(); + +private: + + void resetException(); + void throwException(); + +#ifdef LIBGNUTLS_VERSION + static ssize_t gnutlsPushFunc(gnutls_transport_ptr_t trspt, const void* data, size_t len); + static ssize_t gnutlsPullFunc(gnutls_transport_ptr_t trspt, void* data, size_t len); + static int gnutlsErrnoFunc(gnutls_transport_ptr_t trspt); +#else + static int gnutlsPushFunc(void* trspt, const void* data, size_t len); + static int gnutlsPullFunc(void* trspt, void* data, size_t len); + static int gnutlsErrnoFunc(void* trspt); +#endif // LIBGNUTLS_VERSION + + + shared_ptr <TLSSession_GnuTLS> m_session; + shared_ptr <socket> m_wrapped; + + bool m_connected; + + byte_t m_buffer[65536]; + + exception* m_ex; + + unsigned int m_status; + int m_errno; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSOCKET_GNUTLS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp new file mode 100644 index 0000000..c7b1013 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp @@ -0,0 +1,169 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/platform.hpp" + +#include <openssl/opensslv.h> + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +# define OPENSSL_API_COMPAT 0x10100000L +#endif + +#include <openssl/ssl.h> +#include <openssl/rand.h> +#include <openssl/crypto.h> +#include <openssl/err.h> + +#if OPENSSL_VERSION_NUMBER >= 0x0907000L +# include <openssl/conf.h> +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +# include "vmime/utility/sync/autoLock.hpp" +# include "vmime/utility/sync/criticalSection.hpp" +#endif + + +// OpenSSL locking callbacks for multithreading support (< v1.1 only) +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +namespace { + +vmime::shared_ptr <vmime::utility::sync::criticalSection >* g_openSSLMutexes = NULL; + +extern "C" void VMime_OpenSSLCallback_lock(int mode, int n, const char* /* file */, int /* line */) { + + if (mode & CRYPTO_LOCK) { + g_openSSLMutexes[n]->lock(); + } else { + g_openSSLMutexes[n]->unlock(); + } +} + +extern "C" unsigned long VMime_OpenSSLCallback_id() { + + return vmime::platform::getHandler()->getThreadId(); +} + +} + +#endif + + +namespace vmime { +namespace net { +namespace tls { + + +OpenSSLInitializer::autoInitializer::autoInitializer() { + + // The construction of this unique 'oneTimeInitializer' object will be triggered + // by the 'autoInitializer' objects from the other translation units + static OpenSSLInitializer::oneTimeInitializer oneTimeInitializer; +} + + +OpenSSLInitializer::autoInitializer::~autoInitializer() { + +} + + +OpenSSLInitializer::oneTimeInitializer::oneTimeInitializer() { + + initialize(); +} + + +OpenSSLInitializer::oneTimeInitializer::~oneTimeInitializer() { + + uninitialize(); +} + + +// static +void OpenSSLInitializer::initialize() { + +#if OPENSSL_VERSION_NUMBER >= 0x0907000L && OPENSSL_VERSION_NUMBER < 0x10100000L + OPENSSL_config(NULL); +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + + int numMutexes = CRYPTO_num_locks(); + g_openSSLMutexes = new shared_ptr <vmime::utility::sync::criticalSection>[numMutexes]; + + for (int i = 0 ; i < numMutexes ; ++i) { + g_openSSLMutexes[i] = vmime::platform::getHandler()->createCriticalSection(); + } + + CRYPTO_set_locking_callback(VMime_OpenSSLCallback_lock); + CRYPTO_set_id_callback(VMime_OpenSSLCallback_id); +#endif + + // Seed the RNG, in case /dev/urandom is not available. Explicitely calling + // RAND_seed() even though /dev/urandom is available is harmless. + enum { + SEEDSIZE = 256 + }; + + unsigned char seed[SEEDSIZE]; + vmime::platform::getHandler()->generateRandomBytes(seed, SEEDSIZE); + RAND_seed(seed, SEEDSIZE); +} + + +// static +void OpenSSLInitializer::uninitialize() { + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_cleanup(); + ERR_free_strings(); + + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + delete [] g_openSSLMutexes; + g_openSSLMutexes = NULL; +#endif + +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp new file mode 100644 index 0000000..3b8496d --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp @@ -0,0 +1,90 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED +#define VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + +#include <vector> + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/utility/sync/criticalSection.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Class responsible for setting up OpenSSL + */ +class OpenSSLInitializer { + +public: + + /** Automatically initialize OpenSSL + */ + struct autoInitializer { + + autoInitializer(); + ~autoInitializer(); + }; + +protected: + + struct oneTimeInitializer { + + oneTimeInitializer(); + ~oneTimeInitializer(); + }; + + + /** Initializes the OpenSSL lib + */ + static void initialize(); + + /** Shutdown the OpenSSL lib + */ + static void uninitialize(); +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp new file mode 100644 index 0000000..ea22f1c --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp @@ -0,0 +1,112 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/base.hpp" +#include "vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp" + +#include <openssl/ssl.h> +#include <openssl/err.h> + + +namespace vmime { +namespace net { +namespace tls { + + +TLSProperties::TLSProperties() + : m_data(make_shared <TLSProperties_OpenSSL>()) { + + setCipherSuite(CIPHERSUITE_DEFAULT); +} + + +TLSProperties::TLSProperties(const TLSProperties& props) + : object(), + m_data(make_shared <TLSProperties_OpenSSL>()) { + + *dynamicCast <TLSProperties_OpenSSL>(m_data) = *dynamicCast <TLSProperties_OpenSSL>(props.m_data); +} + + +void TLSProperties::setCipherSuite(const GenericCipherSuite cipherSuite) { + + switch (cipherSuite) { + + case CIPHERSUITE_HIGH: + + setCipherSuite("HIGH:!ADH:@STRENGTH"); + break; + + case CIPHERSUITE_MEDIUM: + + setCipherSuite("MEDIUM:!ADH:@STRENGTH"); + break; + + case CIPHERSUITE_LOW: + + setCipherSuite("LOW:!ADH:@STRENGTH"); + break; + + default: + case CIPHERSUITE_DEFAULT: + + setCipherSuite("DEFAULT:!ADH:@STRENGTH"); + break; + } +} + + +void TLSProperties::setCipherSuite(const string& cipherSuite) { + + dynamicCast <TLSProperties_OpenSSL>(m_data)->cipherSuite = cipherSuite; +} + + +const string TLSProperties::getCipherSuite() const { + + return dynamicCast <TLSProperties_OpenSSL>(m_data)->cipherSuite; +} + + + +TLSProperties_OpenSSL& TLSProperties_OpenSSL::operator=(const TLSProperties_OpenSSL& other) { + + cipherSuite = other.cipherSuite; + + return *this; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp new file mode 100644 index 0000000..8304df2 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp @@ -0,0 +1,68 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSProperties_OpenSSL : public object { + +public: + + TLSProperties_OpenSSL& operator=(const TLSProperties_OpenSSL& other); + + + string cipherSuite; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp new file mode 100644 index 0000000..019341c --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp @@ -0,0 +1,147 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" +#include "vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp" +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/security/cert/certificateException.hpp" + +#include <openssl/ssl.h> +#include <openssl/err.h> + + +namespace vmime { +namespace net { +namespace tls { + + +static OpenSSLInitializer::autoInitializer openSSLInitializer; + + +// static +shared_ptr <TLSSession> TLSSession::create( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) { + + return make_shared <TLSSession_OpenSSL>(cv, props); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL( + const shared_ptr <vmime::security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) + : m_sslctx(0), + m_certVerifier(cv), + m_props(props) { + + m_sslctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_options(m_sslctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(m_sslctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_cipher_list(m_sslctx, m_props->getCipherSuite().c_str()); + SSL_CTX_set_session_cache_mode(m_sslctx, SSL_SESS_CACHE_OFF); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL(const TLSSession_OpenSSL&) + : TLSSession() { + + // Not used +} + + +TLSSession_OpenSSL::~TLSSession_OpenSSL() { + + SSL_CTX_free(m_sslctx); +} + + +shared_ptr <TLSSocket> TLSSession_OpenSSL::getSocket(const shared_ptr <socket>& sok) { + + return TLSSocket::wrap(dynamicCast <TLSSession>(shared_from_this()), sok); +} + + +shared_ptr <security::cert::certificateVerifier> TLSSession_OpenSSL::getCertificateVerifier() { + + return m_certVerifier; +} + + +void TLSSession_OpenSSL::usePrivateKeyFile(const vmime::string& keyfile) { + + ERR_clear_error(); + + if (SSL_CTX_use_PrivateKey_file(m_sslctx, keyfile.c_str(), SSL_FILETYPE_PEM) != 1) { + + unsigned long errCode = ERR_get_error(); + char buffer[256]; + ERR_error_string_n(errCode, buffer, sizeof(buffer)); + vmime::string sslErr(buffer); + std::ostringstream oss; + oss << "Error loading private key from file " << keyfile; + oss << " - msg: " << sslErr; + throw security::cert::certificateException(oss.str()); + } +} + + +void TLSSession_OpenSSL::useCertificateChainFile(const vmime::string& chainFile) { + + ERR_clear_error(); + + if (SSL_CTX_use_certificate_chain_file(m_sslctx, chainFile.c_str()) != 1) { + + unsigned long errCode = ERR_get_error(); + char buffer[256]; + ERR_error_string_n(errCode, buffer, sizeof(buffer)); + vmime::string sslErr(buffer); + std::ostringstream oss; + oss << "Error loading certificate from file " << chainFile; + oss << " - msg: " << sslErr; + throw security::cert::certificateException(oss.str()); + } +} + + +SSL_CTX* TLSSession_OpenSSL::getContext() const { + + return m_sslctx; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp new file mode 100644 index 0000000..518216b --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp @@ -0,0 +1,109 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSession.hpp" +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSProperties.hpp" + + +#include <openssl/ssl.h> + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_OpenSSL : public TLSSession { + + friend class TLSSocket_OpenSSL; + +public: + + TLSSession_OpenSSL( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props + ); + + ~TLSSession_OpenSSL(); + + + shared_ptr <TLSSocket> getSocket(const shared_ptr <socket>& sok); + + shared_ptr <security::cert::certificateVerifier> getCertificateVerifier(); + + + /** Set the private key to use if server requires a client certificate. + * + * @param keyfile path to the private key in PEM format + */ + void usePrivateKeyFile(const vmime::string& keyfile); + + /** Supply the certificate chain to present if requested by server. + * + * @param chainFile File in PEM format holding certificate chain + */ + void useCertificateChainFile(const vmime::string& chainFile); + + /** Get a pointer to the SSL_CTX used for this session. + * + * @return the SSL_CTX used for all connections created with this session + */ + SSL_CTX* getContext() const; + +private: + + TLSSession_OpenSSL(const TLSSession_OpenSSL&); + + SSL_CTX* m_sslctx; + + shared_ptr <security::cert::certificateVerifier> m_certVerifier; + shared_ptr <TLSProperties> m_props; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp new file mode 100644 index 0000000..978f0ca --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -0,0 +1,761 @@ +// +// 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_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp" +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include <vector> +#include <cstring> + + +namespace vmime { +namespace net { +namespace tls { + + +static OpenSSLInitializer::autoInitializer openSSLInitializer; + + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +// static +BIO_METHOD TLSSocket_OpenSSL::sm_customBIOMethod = { + 100 | BIO_TYPE_SOURCE_SINK, + "vmime::socket glue", + TLSSocket_OpenSSL::bio_write, + TLSSocket_OpenSSL::bio_read, + TLSSocket_OpenSSL::bio_puts, + NULL, // gets + TLSSocket_OpenSSL::bio_ctrl, + TLSSocket_OpenSSL::bio_create, + TLSSocket_OpenSSL::bio_destroy, + 0 +}; + +#define BIO_set_init(b, val) b->init = val +#define BIO_set_data(b, val) b->ptr = val +#define BIO_set_num(b, val) b->num = val +#define BIO_set_flags(b, val) b->flags = val +#define BIO_set_shutdown(b, val) b->shutdown = val +#define BIO_get_init(b) b->init +#define BIO_get_data(b) b->ptr +#define BIO_get_shutdown(b) b->shutdown + +#else + +#define BIO_set_num(b, val) + +#endif + + + +// static +shared_ptr <TLSSocket> TLSSocket::wrap( + const shared_ptr <TLSSession>& session, + const shared_ptr <socket>& sok +) { + + return make_shared <TLSSocket_OpenSSL>(dynamicCast <TLSSession_OpenSSL>(session), sok); +} + + +TLSSocket_OpenSSL::TLSSocket_OpenSSL( + const shared_ptr <TLSSession_OpenSSL>& session, + const shared_ptr <socket>& sok +) + : m_session(session), + m_wrapped(sok), + m_connected(false), + m_ssl(0), + m_status(0), + m_ex() { + +} + + +TLSSocket_OpenSSL::~TLSSocket_OpenSSL() { + + try { + disconnect(); + } catch (...) { + // Don't throw in destructor + } +} + + +void TLSSocket_OpenSSL::createSSLHandle() { + + if (m_wrapped->isConnected()) { + string peerName = getPeerName(); + + if (peerName.empty()) { + throw exceptions::tls_exception("Unknown host name, will not be able to set SNI"); + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + + BIO* sockBio = BIO_new(&sm_customBIOMethod); + sockBio->ptr = this; + sockBio->init = 1; + +#else + + BIO_METHOD* bioMeth = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "vmime::socket glue"); + + if (!bioMeth) { + BIO_meth_free(bioMeth); + throw exceptions::tls_exception("BIO_meth_new() failed"); + } + + BIO_meth_set_write(bioMeth, TLSSocket_OpenSSL::bio_write); + BIO_meth_set_read(bioMeth, TLSSocket_OpenSSL::bio_read); + BIO_meth_set_puts(bioMeth, TLSSocket_OpenSSL::bio_puts); + BIO_meth_set_ctrl(bioMeth, TLSSocket_OpenSSL::bio_ctrl); + BIO_meth_set_create(bioMeth, TLSSocket_OpenSSL::bio_create); + BIO_meth_set_destroy(bioMeth, TLSSocket_OpenSSL::bio_destroy); + + BIO* sockBio = BIO_new(bioMeth); + BIO_set_data(sockBio, this); + BIO_set_init(sockBio, 1); + +#endif + + if (!sockBio) { + throw exceptions::tls_exception("BIO_new() failed"); + } + + m_ssl = SSL_new(m_session->getContext()); + + if (!m_ssl) { + BIO_free(sockBio); + throw exceptions::tls_exception("Cannot create SSL object"); + } + + SSL_set_bio(m_ssl, sockBio, sockBio); + SSL_set_tlsext_host_name(m_ssl, peerName.c_str()); + SSL_set_connect_state(m_ssl); + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + } else { + + throw exceptions::tls_exception("Unconnected socket error"); + } +} + + +void TLSSocket_OpenSSL::connect(const string& address, const port_t port) { + + try { + + m_wrapped->connect(address, port); + + createSSLHandle(); + + handshake(); + + } catch (...) { + + disconnect(); + throw; + } +} + + +void TLSSocket_OpenSSL::disconnect() { + + if (m_ssl) { + + // Don't shut down the socket more than once. + int shutdownState = SSL_get_shutdown(m_ssl); + bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; + + if (!shutdownSent) { + SSL_shutdown(m_ssl); + } + + SSL_free(m_ssl); + m_ssl = 0; + } + + if (m_connected) { + m_connected = false; + m_wrapped->disconnect(); + } +} + + +bool TLSSocket_OpenSSL::isConnected() const { + + return m_wrapped->isConnected() && m_connected; +} + + +size_t TLSSocket_OpenSSL::getBlockSize() const { + + return 16384; // 16 KB +} + + +const string TLSSocket_OpenSSL::getPeerName() const { + + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_OpenSSL::getPeerAddress() const { + + return m_wrapped->getPeerAddress(); +} + + +shared_ptr <timeoutHandler> TLSSocket_OpenSSL::getTimeoutHandler() { + + return m_wrapped->getTimeoutHandler(); +} + + +void TLSSocket_OpenSSL::setTracer(const shared_ptr <net::tracer>& tracer) { + + m_wrapped->setTracer(tracer); +} + + +shared_ptr <net::tracer> TLSSocket_OpenSSL::getTracer() { + + return m_wrapped->getTracer(); +} + + +bool TLSSocket_OpenSSL::waitForRead(const int msecs) { + + return m_wrapped->waitForRead(msecs); +} + + +bool TLSSocket_OpenSSL::waitForWrite(const int msecs) { + + return m_wrapped->waitForWrite(msecs); +} + + +void TLSSocket_OpenSSL::receive(string& buffer) { + + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); + + if (size != 0) { + buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); + } else { + buffer.clear(); + } +} + + +void TLSSocket_OpenSSL::send(const string& buffer) { + + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); +} + + +void TLSSocket_OpenSSL::send(const char* str) { + + sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str)); +} + + +size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + ERR_clear_error(); + int rc = SSL_read(m_ssl, buffer, static_cast <int>(count)); + + if (m_ex.get()) { + internalThrow(); + } + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_WRITE) { + m_status |= STATUS_WANT_WRITE; + return 0; + } else if (error == SSL_ERROR_WANT_READ) { + m_status |= STATUS_WANT_READ; + return 0; + } + + handleError(rc); + } + + return rc; +} + + +void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + for (size_t size = count ; size > 0 ; ) { + + ERR_clear_error(); + int rc = SSL_write(m_ssl, buffer, static_cast <int>(size)); + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_READ) { + m_wrapped->waitForRead(); + continue; + } else if (error == SSL_ERROR_WANT_WRITE) { + m_wrapped->waitForWrite(); + continue; + } + + handleError(rc); + + } else { + + buffer += rc; + size -= rc; + } + } +} + + +size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + ERR_clear_error(); + int rc = SSL_write(m_ssl, buffer, static_cast <int>(count)); + + if (m_ex.get()) { + internalThrow(); + } + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_WRITE) { + m_status |= STATUS_WANT_WRITE; + return 0; + } else if (error == SSL_ERROR_WANT_READ) { + m_status |= STATUS_WANT_READ; + return 0; + } + + handleError(rc); + } + + return rc; +} + + +void TLSSocket_OpenSSL::handshake() { + + shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); + + if (toHandler) { + toHandler->resetTimeOut(); + } + + if (getTracer()) { + getTracer()->traceSend("Beginning SSL/TLS handshake"); + } + + // Start handshaking process + if (!m_ssl) { + createSSLHandle(); + } + + try { + + int rc; + + ERR_clear_error(); + + while ((rc = SSL_do_handshake(m_ssl)) <= 0) { + + const int err = SSL_get_error(m_ssl, rc); + + if (err == SSL_ERROR_WANT_READ) { + m_wrapped->waitForRead(); + } else if (err == SSL_ERROR_WANT_WRITE) { + m_wrapped->waitForWrite(); + } else { + handleError(rc); + } + + // Check whether the time-out delay is elapsed + if (toHandler && toHandler->isTimeOut()) { + + if (!toHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + toHandler->resetTimeOut(); + } + + ERR_clear_error(); + } + + } catch (...) { + + throw; + } + + // Verify server's certificate(s) + shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); + + if (!certs) { + throw exceptions::tls_exception("No peer certificate."); + } + + m_session->getCertificateVerifier()->verify(certs, getPeerName()); + + m_connected = true; +} + + +shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates() { + + if (getTracer()) { + getTracer()->traceSend("Getting peer certificates"); + } + + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(m_ssl); + + if (chain == NULL) { + return null; + } + + int certCount = sk_X509_num(chain); + + if (certCount == 0) { + return null; + } + + bool error = false; + std::vector <shared_ptr <security::cert::certificate> > certs; + + for (int i = 0; i < certCount && !error; i++) { + + shared_ptr <vmime::security::cert::X509Certificate> cert = + vmime::security::cert::X509Certificate_OpenSSL::importInternal(sk_X509_value(chain, i)); + + if (cert) { + certs.push_back(cert); + } else { + error = true; + } + } + + if (error) { + return null; + } + + return make_shared <security::cert::certificateChain>(certs); +} + + +void TLSSocket_OpenSSL::internalThrow() { + + if (m_ex.get()) { + throw *m_ex; + } +} + + +void TLSSocket_OpenSSL::handleError(int rc) { + + if (rc > 0) { + return; + } + + internalThrow(); + + int sslError = SSL_get_error(m_ssl, rc); + long lastError = ERR_get_error(); + + switch (sslError) { + + case SSL_ERROR_ZERO_RETURN: + + disconnect(); + return; + + case SSL_ERROR_SYSCALL: { + + if (lastError == 0) { + + if (rc == 0) { + + throw exceptions::tls_exception("SSL connection unexpectedly closed"); + + } else { + + std::ostringstream oss; + oss << "The BIO reported an error: " << rc; + oss.flush(); + throw exceptions::tls_exception(oss.str()); + } + } + + break; + } + + case SSL_ERROR_WANT_READ: + + BIO_set_retry_read(SSL_get_rbio(m_ssl)); + break; + + case SSL_ERROR_WANT_WRITE: + + BIO_set_retry_write(SSL_get_wbio(m_ssl)); + break; + + // This happens only for BIOs of type BIO_s_connect() or BIO_s_accept() + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + // SSL_CTX_set_client_cert_cb related, not used + case SSL_ERROR_WANT_X509_LOOKUP: + case SSL_ERROR_SSL: + default: + + if (lastError == 0) { + + throw exceptions::tls_exception("Unexpected SSL IO error"); + + } else { + + char buffer[256]; + ERR_error_string_n(lastError, buffer, sizeof(buffer)); + vmime::string msg(buffer); + throw exceptions::tls_exception(msg); + } + + break; + } +} + + +unsigned int TLSSocket_OpenSSL::getStatus() const { + + return m_status; +} + + +// Implementation of custom BIO methods + + +// static +int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) { + + BIO_clear_retry_flags(bio); + + if (buf == NULL || len <= 0) { + return -1; + } + + TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); + + if (!BIO_get_init(bio) || !sok) { + return -1; + } + + try { + + const size_t n = sok->m_wrapped->sendRawNonBlocking( + reinterpret_cast <const byte_t*>(buf), len + ); + + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { + BIO_set_retry_write(bio); + return -1; + } + + return static_cast <int>(n); + + } catch (exception& e) { + + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } +} + + +// static +int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) { + + BIO_clear_retry_flags(bio); + + if (buf == NULL || len <= 0) { + return -1; + } + + TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); + + if (!BIO_get_init(bio) || !sok) { + return -1; + } + + try { + + const size_t n = sok->m_wrapped->receiveRaw( + reinterpret_cast <byte_t*>(buf), len + ); + + if (n == 0 || sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { + BIO_set_retry_read(bio); + return -1; + } + + return static_cast <int>(n); + + } catch (exception& e) { + + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } +} + + +// static +int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) { + + return bio_write(bio, str, static_cast <int>(strlen(str))); +} + + +// static +long TLSSocket_OpenSSL::bio_ctrl(BIO* bio, int cmd, long num, void* /* ptr */) { + + long ret = 1; + + switch (cmd) { + + case BIO_CTRL_INFO: + + ret = 0; + break; + + case BIO_CTRL_GET_CLOSE: + + ret = BIO_get_shutdown(bio); + break; + + case BIO_CTRL_SET_CLOSE: + + BIO_set_shutdown(bio, static_cast <int>(num)); + break; + + case BIO_CTRL_PENDING: + case BIO_CTRL_WPENDING: + + ret = 0; + break; + + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + + ret = 1; + break; + + default: + + ret = 0; + break; + } + + return ret; +} + + +// static +int TLSSocket_OpenSSL::bio_create(BIO* bio) { + + BIO_set_init(bio, 0); + BIO_set_num(bio, 0); + BIO_set_data(bio, NULL); + BIO_set_flags(bio, 0); + + return 1; +} + + +// static +int TLSSocket_OpenSSL::bio_destroy(BIO* bio) { + + if (!bio) { + return 0; + } + + if (BIO_get_shutdown(bio)) { + BIO_set_data(bio, NULL); + BIO_set_init(bio, 0); + BIO_set_flags(bio, 0); + } + + return 1; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp new file mode 100644 index 0000000..e30df68 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -0,0 +1,142 @@ +// +// 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. +// + +#ifndef VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/TLSSocket.hpp" + +#include <memory> + +#include <openssl/ssl.h> + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSession_OpenSSL; + + +class TLSSocket_OpenSSL : public TLSSocket { + +public: + + TLSSocket_OpenSSL( + const shared_ptr <TLSSession_OpenSSL>& session, + const shared_ptr <socket>& sok + ); + + ~TLSSocket_OpenSSL(); + + + void handshake(); + + shared_ptr <security::cert::certificateChain> getPeerCertificates(); + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + bool isConnected() const; + + bool waitForRead(const int msecs = 30000); + bool waitForWrite(const int msecs = 30000); + + void receive(string& buffer); + size_t receiveRaw(byte_t* buffer, const size_t count); + + void send(const string& buffer); + void send(const char* str); + void sendRaw(const byte_t* buffer, const size_t count); + size_t sendRawNonBlocking(const byte_t* buffer, const size_t count); + + size_t getBlockSize() const; + + unsigned int getStatus() const; + + const string getPeerName() const; + const string getPeerAddress() const; + + shared_ptr <timeoutHandler> getTimeoutHandler(); + + void setTracer(const shared_ptr <net::tracer>& tracer); + shared_ptr <net::tracer> getTracer(); + +private: + + static BIO_METHOD sm_customBIOMethod; + + static int bio_write(BIO* bio, const char* buf, int len); + static int bio_read(BIO* bio, char* buf, int len); + static int bio_puts(BIO* bio, const char* str); + static int bio_gets(BIO* bio, char* buf, int len); + static long bio_ctrl(BIO* bio, int cmd, long num, void* ptr); + static int bio_create(BIO* bio); + static int bio_destroy(BIO* bio); + + void createSSLHandle(); + + void internalThrow(); + void handleError(int rc); + + + shared_ptr <TLSSession_OpenSSL> m_session; + + shared_ptr <socket> m_wrapped; + + bool m_connected; + + byte_t m_buffer[65536]; + + SSL* m_ssl; + + unsigned int m_status; + + // Last exception thrown from C BIO functions + scoped_ptr <exception> m_ex; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tracer.cpp b/vmime-master/src/vmime/net/tracer.cpp new file mode 100644 index 0000000..66afb36 --- /dev/null +++ b/vmime-master/src/vmime/net/tracer.cpp @@ -0,0 +1,74 @@ +// +// 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 + + +#include "tracer.hpp" + + +#include <sstream> + + +namespace vmime { +namespace net { + + +void tracer::traceReceiveBytes(const size_t count, const string& state) { + + std::ostringstream oss; + oss << "{..."; + + if (!state.empty()) { + oss << state << ": "; + } + + oss << count << " bytes of data...}"; + + traceReceive(oss.str()); +} + + +void tracer::traceSendBytes(const size_t count, const string& state) { + + std::ostringstream oss; + oss << "{..."; + + if (!state.empty()) { + oss << state << ": "; + } + + oss << count << " bytes of data...}"; + + traceSend(oss.str()); +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime-master/src/vmime/net/tracer.hpp b/vmime-master/src/vmime/net/tracer.hpp new file mode 100644 index 0000000..7472463 --- /dev/null +++ b/vmime-master/src/vmime/net/tracer.hpp @@ -0,0 +1,110 @@ +// +// 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. +// + +#ifndef VMIME_NET_TRACER_HPP_INCLUDED +#define VMIME_NET_TRACER_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/base.hpp" + + +namespace vmime { +namespace net { + + +class service; + + +/** Base class for an object used to trace network communication + * between the client and the server. + */ +class VMIME_EXPORT tracer : public object { + +public: + + virtual ~tracer() { + + } + + /** Trace raw bytes which have been received. + * + * @param count number of bytes + * @param state protocol state (eg. "SASL exchange"), or empty + */ + virtual void traceReceiveBytes(const size_t count, const string& state = ""); + + /** Trace raw bytes which have been sent. + * + * @param count number of bytes + * @param state protocol state (eg. "SASL exchange"), or empty + */ + virtual void traceSendBytes(const size_t count, const string& state = ""); + + /** Trace a command line which has been sent. + * + * @param line command line + */ + virtual void traceSend(const string& line) = 0; + + /** Trace a response line which has been received. + * + * @param line response line + */ + virtual void traceReceive(const string& line) = 0; +}; + + +/** A class to create 'tracer' objects. + */ +class VMIME_EXPORT tracerFactory : public object { + +public: + + virtual ~tracerFactory() { + + } + + /** Creates a tracer for the specified service. + * + * @param serv messaging service + * @param connectionId an identifier for the connection to distinguate between + * different connections used by a service + * @return a new tracer + */ + virtual shared_ptr <tracer> create(const shared_ptr <service>& serv, const int connectionId) = 0; +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_TRACER_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/transport.cpp b/vmime-master/src/vmime/net/transport.cpp new file mode 100644 index 0000000..0991302 --- /dev/null +++ b/vmime-master/src/vmime/net/transport.cpp @@ -0,0 +1,265 @@ +// +// 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 + + +#include "vmime/net/transport.hpp" + +#include "vmime/utility/stream.hpp" +#include "vmime/mailboxList.hpp" +#include "vmime/message.hpp" +#include "vmime/dateTime.hpp" +#include "vmime/messageId.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace net { + + +transport::transport( + const shared_ptr <session>& sess, + const serviceInfos& infos, + const shared_ptr <security::authenticator>& auth +) + : service(sess, infos, auth) { + +} + + +shared_ptr <headerField> transport::processHeaderField(const shared_ptr <headerField>& field) { + + if (utility::stringUtils::isStringEqualNoCase(field->getName(), fields::BCC)) { + + // Remove Bcc headers from the message, as required by the RFC. + // Some SMTP server automatically strip this header (Postfix, qmail), + // and others have an option for this (Exim). + return null; + + } else if (utility::stringUtils::isStringEqualNoCase(field->getName(), fields::RETURN_PATH)) { + + // RFC-2821: Return-Path header is added by the final transport system + // that delivers the message to its recipient. Then, it should not be + // transmitted to MSA. + return null; + + } else if (utility::stringUtils::isStringEqualNoCase(field->getName(), fields::ORIGINAL_RECIPIENT)) { + + // RFC-2298: Delivering MTA may add the Original-Recipient header and + // discard existing one; so, no need to send it. + return null; + } + + // Leave the header field as is + return field; +} + + +void transport::processHeader(const shared_ptr <header>& header) { + + if (header->getFieldCount() == 0) { + return; + } + + // Remove/replace fields + for (size_t idx = header->getFieldCount() ; idx != 0 ; --idx) { + + shared_ptr <headerField> field = header->getFieldAt(idx - 1); + shared_ptr <headerField> newField = processHeaderField(field); + + if (newField == NULL) { + header->removeField(field); + } else if (newField != field) { + header->replaceField(field, newField); + } + } + + // Add missing header fields + // -- Date + if (!header->hasField(fields::DATE)) { + header->Date()->setValue(datetime::now()); + } + + // -- Mime-Version + if (!header->hasField(fields::MIME_VERSION)) { + header->MimeVersion()->setValue(string(SUPPORTED_MIME_VERSION)); + } + + // -- Message-Id + if (!header->hasField(fields::MESSAGE_ID)) { + header->MessageId()->setValue(messageId::generateId()); + } +} + + +static void extractMailboxes( + mailboxList& recipients, + const addressList& list +) { + + for (size_t i = 0 ; i < list.getAddressCount() ; ++i) { + + shared_ptr <mailbox> mbox = dynamicCast <mailbox>(list.getAddressAt(i)->clone()); + + if (mbox) { + recipients.appendMailbox(mbox); + } + } +} + + +void transport::send( + const shared_ptr <vmime::message>& msg, + utility::progressListener* progress, + const dsnAttributes& dsnAttrs +) { + + // Extract expeditor + shared_ptr <mailbox> fromMbox = + msg->getHeader()->findFieldValue <mailbox>(fields::FROM); + + if (!fromMbox) { + throw exceptions::no_expeditor(); + } + + mailbox expeditor = *fromMbox; + + // Extract sender + shared_ptr <mailbox> senderMbox = + msg->getHeader()->findFieldValue <mailbox>(fields::SENDER); + + mailbox sender; + + if (!senderMbox) { + sender = expeditor; + } else { + sender = *senderMbox; + } + + // Extract recipients + mailboxList recipients; + + // -- "To" field + shared_ptr <addressList> addresses = + msg->getHeader()->findFieldValue <addressList>(fields::TO); + + if (addresses) { + extractMailboxes(recipients, *addresses); + } + + // -- "Cc" field + addresses = msg->getHeader()->findFieldValue <addressList>(fields::CC); + + if (addresses) { + extractMailboxes(recipients, *addresses); + } + + // -- "Bcc" field + addresses = msg->getHeader()->findFieldValue <addressList>(fields::BCC); + + if (addresses) { + extractMailboxes(recipients, *addresses); + } + + // Process message header by removing fields that should be removed + // before transmitting the message to MSA, and adding missing fields + // which are required/recommended by the RFCs. + shared_ptr <header> hdr = vmime::clone(msg->getHeader()); + processHeader(hdr); + + // To avoid cloning message body (too much overhead), use processed + // header during the time we are generating the message to a stream. + // Revert it back to original header after. + struct XChangeMsgHeader { + + XChangeMsgHeader( + const shared_ptr <vmime::message>& _msg, + const shared_ptr <vmime::header>& _hdr + ) + : msg(_msg), + hdr(msg->getHeader()) { + + // Set new header + msg->setHeader(_hdr); + } + + ~XChangeMsgHeader() { + + // Revert original header + msg->setHeader(hdr); + } + + private: + + shared_ptr <vmime::message> msg; + shared_ptr <vmime::header> hdr; + + } headerExchanger(msg, hdr); + + send(msg, expeditor, recipients, progress, sender, dsnAttrs); +} + + +void transport::send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress, + const mailbox& sender, + const dsnAttributes& dsnAttrs +) { + + // Generate the message, "stream" it and delegate the sending + // to the generic send() function. + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const string& str(oss.str()); + + utility::inputStreamStringAdapter isAdapter(str); + + send(expeditor, recipients, isAdapter, str.length(), progress, sender, + dsnAttrs); +} + + +transport::Type transport::getType() const { + + return TYPE_TRANSPORT; +} + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/vmime-master/src/vmime/net/transport.hpp b/vmime-master/src/vmime/net/transport.hpp new file mode 100644 index 0000000..daa4717 --- /dev/null +++ b/vmime-master/src/vmime/net/transport.hpp @@ -0,0 +1,152 @@ +// +// 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. +// + +#ifndef VMIME_NET_TRANSPORT_HPP_INCLUDED +#define VMIME_NET_TRANSPORT_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/dsnAttributes.hpp" +#include "vmime/net/service.hpp" +#include "vmime/utility/stream.hpp" + +#include "vmime/mailboxList.hpp" + + +namespace vmime { + +class header; +class headerField; +class message; +class mailbox; +class mailboxList; + +namespace net { + + +/** A transport service. + * Encapsulate protocols that can send messages. + */ +class VMIME_EXPORT transport : public service { + +protected: + + transport( + const shared_ptr <session>& sess, + const serviceInfos& infos, + const shared_ptr <security::authenticator>& auth + ); + +public: + + /** Send a message over this transport service. + * The default implementation simply generates the whole message into + * a string buffer and "streams" it via a inputStreamStringAdapter. + * + * @param msg message to send + * @param progress progress listener, or NULL if not used + * @param dsnAttributes attributes for Delivery Status Notification (if needed) + */ + virtual void send( + const shared_ptr <vmime::message>& msg, + utility::progressListener* progress = NULL, + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + /** Send a message over this transport service. + * + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param is input stream providing message data (header + body) + * @param size size of the message data + * @param progress progress listener, or NULL if not used + * @param sender envelope sender (if empty, expeditor will be used) + * @param dsnAttributes attributes for Delivery Status Notification (if needed) + */ + virtual void send( + const mailbox& expeditor, + const mailboxList& recipients, + utility::inputStream& is, + const size_t size, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ) = 0; + + /** Send a message over this transport service. + * The default implementation simply generates the whole message into + * a string buffer and "streams" it via a inputStreamStringAdapter. + * + * @param msg message to send + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param progress progress listener, or NULL if not used + * @param sender envelope sender (if empty, expeditor will be used) + * @param dsnAttributes attributes for Delivery Status Notification (if needed) + */ + virtual void send( + const shared_ptr <vmime::message>& msg, + const mailbox& expeditor, + const mailboxList& recipients, + utility::progressListener* progress = NULL, + const mailbox& sender = mailbox(), + const dsnAttributes& dsnAttrs = dsnAttributes() + ); + + + Type getType() const; + +protected: + + /** Called by processHeader(). + * Decides what to do with the specified header field. + * + * @return NULL if the header should be removed, a reference to a new headerField + * if the field is to be replaced, or a reference to the same headerField + * that was passed if the field should be left as is + */ + shared_ptr <headerField> processHeaderField(const shared_ptr <headerField>& field); + + /** Prepares the header before transmitting the message. + * Removes headers that should not be present (eg. "Bcc", "Return-Path"), + * or adds missing headers that are required/recommended by the RFCs. + * The header is modified inline. + * + * @param header headers to process + */ + void processHeader(const shared_ptr <header>& header); +}; + + +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + +#endif // VMIME_NET_TRANSPORT_HPP_INCLUDED |