aboutsummaryrefslogtreecommitdiffstats
path: root/src/vmime/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/vmime/net')
-rw-r--r--src/vmime/net/builtinServices.inl76
-rw-r--r--src/vmime/net/connectionInfos.hpp68
-rw-r--r--src/vmime/net/defaultConnectionInfos.cpp60
-rw-r--r--src/vmime/net/defaultConnectionInfos.hpp66
-rw-r--r--src/vmime/net/events.cpp166
-rw-r--r--src/vmime/net/events.hpp273
-rw-r--r--src/vmime/net/fetchAttributes.cpp95
-rw-r--r--src/vmime/net/fetchAttributes.hpp142
-rw-r--r--src/vmime/net/folder.cpp127
-rw-r--r--src/vmime/net/folder.hpp399
-rw-r--r--src/vmime/net/folderStatus.hpp74
-rw-r--r--src/vmime/net/imap/IMAPConnection.cpp814
-rw-r--r--src/vmime/net/imap/IMAPConnection.hpp163
-rw-r--r--src/vmime/net/imap/IMAPFolder.cpp1511
-rw-r--r--src/vmime/net/imap/IMAPFolder.hpp204
-rw-r--r--src/vmime/net/imap/IMAPFolderStatus.cpp307
-rw-r--r--src/vmime/net/imap/IMAPFolderStatus.hpp124
-rw-r--r--src/vmime/net/imap/IMAPMessage.cpp649
-rw-r--r--src/vmime/net/imap/IMAPMessage.hpp191
-rw-r--r--src/vmime/net/imap/IMAPMessagePart.cpp161
-rw-r--r--src/vmime/net/imap/IMAPMessagePart.hpp92
-rw-r--r--src/vmime/net/imap/IMAPMessagePartContentHandler.cpp216
-rw-r--r--src/vmime/net/imap/IMAPMessagePartContentHandler.hpp87
-rw-r--r--src/vmime/net/imap/IMAPMessageStructure.cpp94
-rw-r--r--src/vmime/net/imap/IMAPMessageStructure.hpp75
-rw-r--r--src/vmime/net/imap/IMAPParser.hpp5617
-rw-r--r--src/vmime/net/imap/IMAPSStore.cpp79
-rw-r--r--src/vmime/net/imap/IMAPSStore.hpp71
-rw-r--r--src/vmime/net/imap/IMAPServiceInfos.cpp137
-rw-r--r--src/vmime/net/imap/IMAPServiceInfos.hpp91
-rw-r--r--src/vmime/net/imap/IMAPStore.cpp267
-rw-r--r--src/vmime/net/imap/IMAPStore.hpp120
-rw-r--r--src/vmime/net/imap/IMAPTag.cpp122
-rw-r--r--src/vmime/net/imap/IMAPTag.hpp79
-rw-r--r--src/vmime/net/imap/IMAPUtils.cpp758
-rw-r--r--src/vmime/net/imap/IMAPUtils.hpp129
-rw-r--r--src/vmime/net/imap/imap.hpp35
-rw-r--r--src/vmime/net/maildir/format/courierMaildirFormat.cpp542
-rw-r--r--src/vmime/net/maildir/format/courierMaildirFormat.hpp121
-rw-r--r--src/vmime/net/maildir/format/kmailMaildirFormat.cpp320
-rw-r--r--src/vmime/net/maildir/format/kmailMaildirFormat.hpp109
-rw-r--r--src/vmime/net/maildir/maildir.hpp34
-rw-r--r--src/vmime/net/maildir/maildirFolder.cpp1254
-rw-r--r--src/vmime/net/maildir/maildirFolder.hpp190
-rw-r--r--src/vmime/net/maildir/maildirFolderStatus.cpp88
-rw-r--r--src/vmime/net/maildir/maildirFolderStatus.hpp76
-rw-r--r--src/vmime/net/maildir/maildirFormat.cpp109
-rw-r--r--src/vmime/net/maildir/maildirFormat.hpp195
-rw-r--r--src/vmime/net/maildir/maildirMessage.cpp369
-rw-r--r--src/vmime/net/maildir/maildirMessage.hpp116
-rw-r--r--src/vmime/net/maildir/maildirMessagePart.cpp155
-rw-r--r--src/vmime/net/maildir/maildirMessagePart.hpp99
-rw-r--r--src/vmime/net/maildir/maildirMessageStructure.cpp98
-rw-r--r--src/vmime/net/maildir/maildirMessageStructure.hpp76
-rw-r--r--src/vmime/net/maildir/maildirServiceInfos.cpp77
-rw-r--r--src/vmime/net/maildir/maildirServiceInfos.hpp71
-rw-r--r--src/vmime/net/maildir/maildirStore.cpp272
-rw-r--r--src/vmime/net/maildir/maildirStore.hpp120
-rw-r--r--src/vmime/net/maildir/maildirUtils.cpp273
-rw-r--r--src/vmime/net/maildir/maildirUtils.hpp151
-rw-r--r--src/vmime/net/message.cpp150
-rw-r--r--src/vmime/net/message.hpp355
-rw-r--r--src/vmime/net/messageSet.cpp369
-rw-r--r--src/vmime/net/messageSet.hpp335
-rw-r--r--src/vmime/net/pop3/POP3Command.cpp230
-rw-r--r--src/vmime/net/pop3/POP3Command.hpp113
-rw-r--r--src/vmime/net/pop3/POP3Connection.cpp675
-rw-r--r--src/vmime/net/pop3/POP3Connection.hpp125
-rw-r--r--src/vmime/net/pop3/POP3Folder.cpp729
-rw-r--r--src/vmime/net/pop3/POP3Folder.hpp157
-rw-r--r--src/vmime/net/pop3/POP3FolderStatus.cpp88
-rw-r--r--src/vmime/net/pop3/POP3FolderStatus.hpp76
-rw-r--r--src/vmime/net/pop3/POP3Message.cpp250
-rw-r--r--src/vmime/net/pop3/POP3Message.hpp121
-rw-r--r--src/vmime/net/pop3/POP3Response.cpp428
-rw-r--r--src/vmime/net/pop3/POP3Response.hpp182
-rw-r--r--src/vmime/net/pop3/POP3SStore.cpp79
-rw-r--r--src/vmime/net/pop3/POP3SStore.hpp71
-rw-r--r--src/vmime/net/pop3/POP3ServiceInfos.cpp143
-rw-r--r--src/vmime/net/pop3/POP3ServiceInfos.hpp93
-rw-r--r--src/vmime/net/pop3/POP3Store.cpp240
-rw-r--r--src/vmime/net/pop3/POP3Store.hpp116
-rw-r--r--src/vmime/net/pop3/POP3Utils.cpp114
-rw-r--r--src/vmime/net/pop3/POP3Utils.hpp86
-rw-r--r--src/vmime/net/pop3/pop3.hpp35
-rw-r--r--src/vmime/net/securedConnectionInfos.hpp55
-rw-r--r--src/vmime/net/sendmail/sendmail.hpp31
-rw-r--r--src/vmime/net/sendmail/sendmailServiceInfos.cpp78
-rw-r--r--src/vmime/net/sendmail/sendmailServiceInfos.hpp71
-rw-r--r--src/vmime/net/sendmail/sendmailTransport.cpp230
-rw-r--r--src/vmime/net/sendmail/sendmailTransport.hpp103
-rw-r--r--src/vmime/net/service.cpp152
-rw-r--r--src/vmime/net/service.hpp233
-rw-r--r--src/vmime/net/serviceFactory.cpp145
-rw-r--r--src/vmime/net/serviceFactory.hpp165
-rw-r--r--src/vmime/net/serviceInfos.cpp167
-rw-r--r--src/vmime/net/serviceInfos.hpp246
-rw-r--r--src/vmime/net/serviceRegistration.inl98
-rw-r--r--src/vmime/net/session.cpp158
-rw-r--r--src/vmime/net/session.hpp178
-rw-r--r--src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp145
-rw-r--r--src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp85
-rw-r--r--src/vmime/net/smtp/SMTPCommand.cpp214
-rw-r--r--src/vmime/net/smtp/SMTPCommand.hpp111
-rw-r--r--src/vmime/net/smtp/SMTPCommandSet.cpp144
-rw-r--r--src/vmime/net/smtp/SMTPCommandSet.hpp105
-rw-r--r--src/vmime/net/smtp/SMTPConnection.cpp618
-rw-r--r--src/vmime/net/smtp/SMTPConnection.hpp129
-rw-r--r--src/vmime/net/smtp/SMTPExceptions.cpp151
-rw-r--r--src/vmime/net/smtp/SMTPExceptions.hpp127
-rw-r--r--src/vmime/net/smtp/SMTPResponse.cpp334
-rw-r--r--src/vmime/net/smtp/SMTPResponse.hpp186
-rw-r--r--src/vmime/net/smtp/SMTPSTransport.cpp79
-rw-r--r--src/vmime/net/smtp/SMTPSTransport.hpp71
-rw-r--r--src/vmime/net/smtp/SMTPServiceInfos.cpp146
-rw-r--r--src/vmime/net/smtp/SMTPServiceInfos.hpp95
-rw-r--r--src/vmime/net/smtp/SMTPTransport.cpp420
-rw-r--r--src/vmime/net/smtp/SMTPTransport.hpp131
-rw-r--r--src/vmime/net/smtp/smtp.hpp33
-rw-r--r--src/vmime/net/socket.hpp184
-rw-r--r--src/vmime/net/store.hpp114
-rw-r--r--src/vmime/net/timeoutHandler.hpp89
-rw-r--r--src/vmime/net/tls/TLSProperties.cpp44
-rw-r--r--src/vmime/net/tls/TLSProperties.hpp105
-rw-r--r--src/vmime/net/tls/TLSSecuredConnectionInfos.cpp72
-rw-r--r--src/vmime/net/tls/TLSSecuredConnectionInfos.hpp84
-rw-r--r--src/vmime/net/tls/TLSSession.cpp48
-rw-r--r--src/vmime/net/tls/TLSSession.hpp93
-rw-r--r--src/vmime/net/tls/TLSSocket.cpp44
-rw-r--r--src/vmime/net/tls/TLSSocket.hpp88
-rw-r--r--src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp113
-rw-r--r--src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp68
-rw-r--r--src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp300
-rw-r--r--src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp91
-rw-r--r--src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp490
-rw-r--r--src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp120
-rw-r--r--src/vmime/net/tls/openssl/OpenSSLInitializer.cpp142
-rw-r--r--src/vmime/net/tls/openssl/OpenSSLInitializer.hpp111
-rw-r--r--src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp112
-rw-r--r--src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp68
-rw-r--r--src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp135
-rw-r--r--src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp108
-rw-r--r--src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp601
-rw-r--r--src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp132
-rw-r--r--src/vmime/net/transport.cpp236
-rw-r--r--src/vmime/net/transport.hpp137
146 files changed, 33377 insertions, 0 deletions
diff --git a/src/vmime/net/builtinServices.inl b/src/vmime/net/builtinServices.inl
new file mode 100644
index 00000000..fa2f3fe3
--- /dev/null
+++ b/src/vmime/net/builtinServices.inl
@@ -0,0 +1,76 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/connectionInfos.hpp b/src/vmime/net/connectionInfos.hpp
new file mode 100644
index 00000000..6c86eeab
--- /dev/null
+++ b/src/vmime/net/connectionInfos.hpp
@@ -0,0 +1,68 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/defaultConnectionInfos.cpp b/src/vmime/net/defaultConnectionInfos.cpp
new file mode 100644
index 00000000..335e8f6f
--- /dev/null
+++ b/src/vmime/net/defaultConnectionInfos.cpp
@@ -0,0 +1,60 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/defaultConnectionInfos.hpp b/src/vmime/net/defaultConnectionInfos.hpp
new file mode 100644
index 00000000..50673bbc
--- /dev/null
+++ b/src/vmime/net/defaultConnectionInfos.hpp
@@ -0,0 +1,66 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/events.cpp b/src/vmime/net/events.cpp
new file mode 100644
index 00000000..a19e1738
--- /dev/null
+++ b/src/vmime/net/events.cpp
@@ -0,0 +1,166 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ (shared_ptr <folder> folder, const Types type, const std::vector <int>& 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 <int>& 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
+ (shared_ptr <folder> folder, const Types type, const std::vector <int>& 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 <int>& 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
+ (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/src/vmime/net/events.hpp b/src/vmime/net/events.hpp
new file mode 100644
index 00000000..a3e952d4
--- /dev/null
+++ b/src/vmime/net/events.hpp
@@ -0,0 +1,273 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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:
+
+ 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(shared_ptr <folder> folder, const Types type, const std::vector <int>& 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 <int>& 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 <int> m_nums;
+};
+
+
+/** Listener for events about the message count in a folder.
+ */
+
+class VMIME_EXPORT messageCountListener
+{
+protected:
+
+ virtual ~messageCountListener() { }
+
+public:
+
+ virtual void messagesAdded(shared_ptr <messageCountEvent> event) = 0;
+ virtual void messagesRemoved(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(shared_ptr <folder> folder, const Types type, const std::vector <int>& 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 <int>& 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 <int> m_nums;
+};
+
+
+/** Listener for events occuring on a message.
+ */
+
+class VMIME_EXPORT messageChangedListener
+{
+protected:
+
+ virtual ~messageChangedListener() { }
+
+public:
+
+ virtual void messageChanged(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(shared_ptr <folder> folder, const Types type, const utility::path& oldPath, const utility::path& newPath);
+
+ /** Return the folder on which the event occured.
+ *
+ * @return folder on which the event occured
+ */
+ 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(shared_ptr <folderEvent> event) = 0;
+ virtual void folderRenamed(shared_ptr <folderEvent> event) = 0;
+ virtual void folderDeleted(shared_ptr <folderEvent> event) = 0;
+};
+
+
+} // events
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_EVENTS_HPP_INCLUDED
diff --git a/src/vmime/net/fetchAttributes.cpp b/src/vmime/net/fetchAttributes.cpp
new file mode 100644
index 00000000..85a41b8c
--- /dev/null
+++ b/src/vmime/net/fetchAttributes.cpp
@@ -0,0 +1,95 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/fetchAttributes.hpp b/src/vmime/net/fetchAttributes.hpp
new file mode 100644
index 00000000..d01e9f50
--- /dev/null
+++ b/src/vmime/net/fetchAttributes.hpp
@@ -0,0 +1,142 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/folder.cpp b/src/vmime/net/folder.cpp
new file mode 100644
index 00000000..1d6f3140
--- /dev/null
+++ b/src/vmime/net/folder.cpp
@@ -0,0 +1,127 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 {
+
+
+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(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(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(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(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/src/vmime/net/folder.hpp b/src/vmime/net/folder.hpp
new file mode 100644
index 00000000..38ba4597
--- /dev/null
+++ b/src/vmime/net/folder.hpp
@@ -0,0 +1,399 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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
+{
+protected:
+
+ folder(const folder&) : object() { }
+ folder() { }
+
+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). */
+ };
+
+ /** Folder types.
+ */
+ enum Types
+ {
+ TYPE_CONTAINS_FOLDERS = (1 << 0), /**< Folder can contain folders. */
+ TYPE_CONTAINS_MESSAGES = (1 << 1), /**< Folder can contain messages. */
+
+ TYPE_UNDEFINED = 9999 /**< Used internally (this should not be returned
+ by the type() function). */
+ };
+
+ /** Folder flags.
+ */
+ enum Flags
+ {
+ FLAG_CHILDREN = (1 << 0), /**< Folder contains subfolders. */
+ FLAG_NO_OPEN = (1 << 1), /**< Folder cannot be open. */
+
+ FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned
+ by the type() function). */
+ };
+
+ /** Return the type of this folder.
+ *
+ * @return folder type (see folder::Types)
+ */
+ virtual int getType() = 0;
+
+ /** Return the flags of this folder.
+ *
+ * @return folder flags (see folder::Flags)
+ */
+ virtual int getFlags() = 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 type folder type (see folder::Types)
+ * @throw exceptions::net_exception if an error occurs
+ */
+ virtual void create(const int type) = 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 int 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 int 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
+ * @param date date/time for the new message (if NULL, the current time is used)
+ * @param progress progress listener, or NULL if not used
+ * @throw exceptions::net_exception if an error occurs
+ */
+ virtual void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, 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
+ * @param date date/time for the new message (if NULL, the current time is used)
+ * @param progress progress listener, or NULL if not used
+ * @throw exceptions::net_exception if an error occurs
+ */
+ virtual void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, 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
+ * @throw exceptions::net_exception if an error occurs
+ */
+ virtual void 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(int& count, int& 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(shared_ptr <message> msg, 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 <int> 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(shared_ptr <events::messageChangedEvent> event);
+ void notifyMessageCount(shared_ptr <events::messageCountEvent> event);
+ void notifyFolder(shared_ptr <events::folderEvent> event);
+ void notifyEvent(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/src/vmime/net/folderStatus.hpp b/src/vmime/net/folderStatus.hpp
new file mode 100644
index 00000000..b94db052
--- /dev/null
+++ b/src/vmime/net/folderStatus.hpp
@@ -0,0 +1,74 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 unsigned int getMessageCount() const = 0;
+
+ /** Returns the number of unseen messages in the folder.
+ *
+ * @return number of unseen messages
+ */
+ virtual unsigned int 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/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp
new file mode 100644
index 00000000..234c2b6a
--- /dev/null
+++ b/src/vmime/net/imap/IMAPConnection.cpp
@@ -0,0 +1,814 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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(shared_ptr <IMAPStore> store, 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)
+{
+}
+
+
+IMAPConnection::~IMAPConnection()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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);
+
+#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_tag = make_shared <IMAPTag>();
+ m_parser = make_shared <IMAPParser>(m_tag, m_socket, m_timeoutHandler);
+
+
+ setState(STATE_NON_AUTHENTICATED);
+
+
+ // Connection greeting
+ //
+ // eg: C: <connection to server>
+ // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready
+
+ std::auto_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());
+ }
+
+#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& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try normal authentication
+ }
+ }
+ catch (exception& e)
+ {
+ internalDisconnect();
+ throw e;
+ }
+ }
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ // Normal authentication
+ const string username = getAuthenticator()->getUsername();
+ const string password = getAuthenticator()->getPassword();
+
+ send(true, "LOGIN " + IMAPUtils::quoteString(username)
+ + " " + IMAPUtils::quoteString(password), true);
+
+ std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ 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 =
+ make_shared <security::sasl::SASLContext>();
+
+ 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();
+
+ send(true, "AUTHENTICATE " + mech->getName(), true);
+
+ for (bool cont = true ; cont ; )
+ {
+ std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ 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
+ {
+ std::vector <IMAPParser::continue_req_or_response_data*>
+ respDataList = resp->continue_req_or_response_data();
+
+ string response;
+ bool hasResponse = false;
+
+ for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
+ {
+ if (respDataList[i]->continue_req())
+ {
+ response = respDataList[i]->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
+ send(false, saslContext->encodeB64(resp, respLen), true);
+
+ // 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
+ send(false, "*", true);
+ }
+ 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
+ {
+ send(true, "STARTTLS", true);
+
+ std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ 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_timeoutHandler);
+
+ 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;
+}
+
+
+void IMAPConnection::invalidateCapabilities()
+{
+ m_capabilities.clear();
+ m_capabilitiesFetched = false;
+}
+
+
+void IMAPConnection::fetchCapabilities()
+{
+ send(true, "CAPABILITY", true);
+
+ std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ 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)
+{
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (size_t i = 0 ; i < respDataList.size() ; ++i)
+ {
+ if (respDataList[i]->response_data() == NULL)
+ continue;
+
+ const IMAPParser::capability_data* capaData =
+ respDataList[i]->response_data()->capability_data();
+
+ if (capaData == NULL)
+ continue;
+
+ processCapabilityResponseData(capaData);
+ return true;
+ }
+
+ return false;
+}
+
+
+void IMAPConnection::processCapabilityResponseData(const IMAPParser::capability_data* capaData)
+{
+ std::vector <string> res;
+
+ std::vector <IMAPParser::capability*> caps = capaData->capabilities();
+
+ for (unsigned int j = 0 ; j < caps.size() ; ++j)
+ {
+ if (caps[j]->auth_type())
+ res.push_back("AUTH=" + caps[j]->auth_type()->name());
+ else
+ res.push_back(utility::stringUtils::toUpper(caps[j]->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())
+ {
+ send(true, "LOGOUT", true);
+
+ m_socket->disconnect();
+ m_socket = null;
+ }
+
+ m_timeoutHandler = null;
+
+ m_state = STATE_LOGOUT;
+
+ m_secured = false;
+ m_cntInfos = null;
+}
+
+
+void IMAPConnection::initHierarchySeparator()
+{
+ send(true, "LIST \"\" \"\"", true);
+
+ std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ 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 std::vector <IMAPParser::continue_req_or_response_data*>& 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() == NULL)
+ continue;
+
+ const IMAPParser::mailbox_data* mailboxData =
+ static_cast <const IMAPParser::response_data*>
+ (respDataList[i]->response_data())->mailbox_data();
+
+ if (mailboxData == NULL || 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::send(bool tag, const string& what, bool end)
+{
+ if (tag && !m_firstTag)
+ ++(*m_tag);
+
+#if VMIME_DEBUG
+ std::ostringstream oss;
+
+ if (tag)
+ {
+ oss << string(*m_tag);
+ oss << " ";
+ }
+
+ oss << what;
+
+ if (end)
+ oss << "\r\n";
+
+ m_socket->send(oss.str());
+#else
+ if (tag)
+ {
+ m_socket->send(*m_tag);
+ m_socket->send(" ");
+ }
+
+ m_socket->send(what);
+
+ if (end)
+ {
+ m_socket->send("\r\n");
+ }
+#endif
+
+ if (tag)
+ m_firstTag = false;
+}
+
+
+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(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;
+}
+
+
+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/src/vmime/net/imap/IMAPConnection.hpp b/src/vmime/net/imap/IMAPConnection.hpp
new file mode 100644
index 00000000..b38d0c27
--- /dev/null
+++ b/src/vmime/net/imap/IMAPConnection.hpp
@@ -0,0 +1,163 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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 VMIME_EXPORT IMAPConnection : public object
+{
+public:
+
+ IMAPConnection(shared_ptr <IMAPStore> store, 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 send(bool tag, const string& what, bool end);
+ 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);
+
+ shared_ptr <security::authenticator> getAuthenticator();
+
+ bool isSecuredConnection() const;
+ shared_ptr <connectionInfos> getConnectionInfos() const;
+
+ shared_ptr <const socket> getSocket() const;
+
+ 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;
+
+
+ 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/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp
new file mode 100644
index 00000000..fb98887c
--- /dev/null
+++ b/src/vmime/net/imap/IMAPFolder.cpp
@@ -0,0 +1,1511 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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, shared_ptr <IMAPStore> store, const int type, const int flags)
+ : 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_type(type), m_flags(flags)
+{
+ store->registerFolder(this);
+
+ m_status = make_shared <IMAPFolderStatus>();
+}
+
+
+IMAPFolder::~IMAPFolder()
+{
+ 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();
+ }
+}
+
+
+int IMAPFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+int IMAPFolder::getType()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (TYPE_CONTAINS_FOLDERS);
+ }
+ else
+ {
+ if (m_type == TYPE_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_type);
+ }
+}
+
+
+int IMAPFolder::getFlags()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Root folder
+ if (m_path.isEmpty())
+ {
+ return (FLAG_CHILDREN | FLAG_NO_OPEN);
+ }
+ else
+ {
+ if (m_flags == FLAG_UNDEFINED)
+ testExistAndGetType();
+
+ return (m_flags);
+ }
+}
+
+
+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::ostringstream oss;
+
+ if (mode == MODE_READ_ONLY)
+ oss << "EXAMINE ";
+ else
+ oss << "SELECT ";
+
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (connection->hierarchySeparator(), getFullPath()));
+
+ if (m_connection->hasCapability("CONDSTORE"))
+ oss << " (CONDSTORE)";
+
+ connection->send(true, oss.str(), true);
+
+ // Read the response
+ std::auto_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");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("SELECT",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional)
+ if (responseData->resp_cond_state())
+ {
+ const IMAPParser::resp_text_code* code =
+ responseData->resp_cond_state()->resp_text()->resp_text_code();
+
+ if (code != NULL)
+ {
+ 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:
+ {
+ m_type = IMAPUtils::folderTypeFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (responseData->mailbox_data()->mailbox_flag_list());
+
+ break;
+ }
+
+ }
+ }
+ }
+
+ processStatusUpdate(resp.get());
+
+ // Check for access mode (read-only or read-write)
+ const IMAPParser::resp_text_code* respTextCode = resp->response_done()->
+ response_tagged()->resp_cond_state()->resp_text()->resp_text_code();
+
+ 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();
+
+ oldConnection->send(true, "CLOSE", true);
+ }
+
+ // 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 int type)
+{
+ 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 (type & TYPE_CONTAINS_FOLDERS)
+ mailbox += m_connection->hierarchySeparator();
+
+ std::ostringstream oss;
+ oss << "CREATE " << IMAPUtils::quoteString(mailbox);
+
+ m_connection->send(true, oss.str(), true);
+
+
+ std::auto_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());
+
+ std::ostringstream oss;
+ oss << "DELETE " << IMAPUtils::quoteString(mailbox);
+
+ m_connection->send(true, oss.str(), true);
+
+
+ std::auto_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() != TYPE_UNDEFINED);
+}
+
+
+int IMAPFolder::testExistAndGetType()
+{
+ m_type = TYPE_UNDEFINED;
+
+ // 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
+
+ std::ostringstream oss;
+ oss << "LIST \"\" ";
+ oss << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+
+ m_connection->send(true, oss.str(), true);
+
+
+ std::auto_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
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ // We are only interested in responses of type "LIST"
+ if (mailboxData != NULL && mailboxData->type() == IMAPParser::mailbox_data::LIST)
+ {
+ // Get the folder type/flags at the same time
+ m_type = IMAPUtils::folderTypeFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+
+ m_flags = IMAPUtils::folderFlagsFromFlags
+ (mailboxData->mailbox_list()->mailbox_flag_list());
+ }
+ }
+
+ return (m_type);
+}
+
+
+bool IMAPFolder::isOpen() const
+{
+ return (m_open);
+}
+
+
+shared_ptr <message> IMAPFolder::getMessage(const int 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;
+
+ if (msgs.isNumberSet())
+ {
+ const std::vector <int> numbers = IMAPUtils::messageSetToNumberList(msgs);
+
+ shared_ptr <IMAPFolder> thisFolder = dynamicCast <IMAPFolder>(shared_from_this());
+
+ for (std::vector <int>::const_iterator it = numbers.begin() ; it != numbers.end() ; ++it)
+ messages.push_back(make_shared <IMAPMessage>(thisFolder, *it));
+ }
+ else if (msgs.isUIDSet())
+ {
+ // 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
+
+ // Prepare command and arguments
+ std::ostringstream cmd;
+ cmd.imbue(std::locale::classic());
+
+ cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs) << " UID";
+
+ // Send the request
+ m_connection->send(true, cmd.str(), true);
+
+ // Get the response
+ std::auto_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
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("UID FETCH ... UID",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "FETCH"
+ if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH)
+ continue;
+
+ // Get Process fetch response for this message
+ const int msgNum = static_cast <int>(messageData->number());
+ message::uid msgUID;
+
+ // Find UID in message attributes
+ const std::vector <IMAPParser::msg_att_item*> atts = messageData->msg_att()->items();
+
+ for (std::vector <IMAPParser::msg_att_item*>::const_iterator
+ it = atts.begin() ; it != atts.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::msg_att_item::UID)
+ {
+ msgUID = (*it)->unique_id()->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;
+}
+
+
+int 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);
+}
+
+
+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
+
+ std::ostringstream oss;
+ oss << "LIST ";
+
+ const string pathString = IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath());
+
+ if (recursive)
+ {
+ oss << IMAPUtils::quoteString(pathString);
+ oss << " *";
+ }
+ else
+ {
+ if (pathString.empty()) // don't add sep for root folder
+ oss << "\"\"";
+ else
+ oss << IMAPUtils::quoteString(pathString + m_connection->hierarchySeparator());
+
+ oss << " %";
+ }
+
+ m_connection->send(true, oss.str(), true);
+
+
+ std::auto_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");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+
+ std::vector <shared_ptr <folder> > v;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("LIST",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST)
+ continue;
+
+ // Get folder path
+ const class IMAPParser::mailbox* 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
+ const class IMAPParser::mailbox_flag_list* mailbox_flag_list =
+ mailboxData->mailbox_list()->mailbox_flag_list();
+
+ v.push_back(make_shared <IMAPFolder>(path, store,
+ IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
+ IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
+ }
+ }
+
+ 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");
+
+ // Build message numbers list
+ std::vector <int> list;
+ list.reserve(msg.size());
+
+ std::map <int, 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
+ const string command = IMAPUtils::buildFetchRequest
+ (m_connection, messageSet::byNumber(list), options);
+
+ m_connection->send(true, command, true);
+
+ // Get the response
+ std::auto_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");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ const size_t total = msg.size();
+ size_t current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ try
+ {
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("FETCH",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "FETCH"
+ if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH)
+ continue;
+
+ // Process fetch response for this message
+ const int num = static_cast <int>(messageData->number());
+
+ std::map <int, 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(shared_ptr <message> msg, const fetchAttributes& options)
+{
+ std::vector <shared_ptr <message> > msgs;
+ msgs.push_back(msg);
+
+ fetchMessages(msgs, options, /* progress */ NULL);
+}
+
+
+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 <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");
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ if (msgs.isUIDSet())
+ command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
+ else
+ command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
+
+ command << " +FLAGS (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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)
+{
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ if (msgs.isUIDSet())
+ command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
+ else
+ command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs);
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD: command << " +FLAGS "; break;
+ case message::FLAG_MODE_REMOVE: command << " -FLAGS "; break;
+ default:
+ case message::FLAG_MODE_SET: command << " FLAGS "; break;
+ }
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (!flagList.empty())
+ {
+ command << flagList;
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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::addMessage(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);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void 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");
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << ' ';
+
+ const string flagList = IMAPUtils::messageFlagList(flags);
+
+ if (flags != message::FLAG_UNDEFINED && !flagList.empty())
+ {
+ command << flagList;
+ command << ' ';
+ }
+
+ if (date != NULL)
+ {
+ command << IMAPUtils::dateTime(*date);
+ command << ' ';
+ }
+
+ command << '{' << size << '}';
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_ptr <IMAPParser::response> resp(m_connection->readResponse());
+
+ bool ok = false;
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respList
+ = resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ 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");
+ }
+
+ // 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, sizeof(buffer));
+ current += read;
+
+ // Put read data into socket output stream
+ m_connection->sendRaw(buffer, read);
+
+ // Notify progress
+ if (progress)
+ progress->progress(current, total);
+ }
+
+ m_connection->send(false, "", true);
+
+ if (progress)
+ progress->stop(total);
+
+ // Get the response
+ std::auto_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(resp.get());
+}
+
+
+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
+ m_connection->send(true, "EXPUNGE", true);
+
+ // Get the response
+ std::auto_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();
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ command << "RENAME ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath())) << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), newPath));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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());
+}
+
+
+void 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");
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ command << "COPY " << IMAPUtils::messageSetToSequenceSet(set) << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), dest));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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());
+}
+
+
+void IMAPFolder::status(int& count, int& 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 request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ command << "STATUS ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+ command << " (";
+
+ command << "MESSAGES" << ' ' << "UNSEEN" << ' ' << "UIDNEXT" << ' ' << "UIDVALIDITY";
+
+ if (m_connection->hasCapability("CONDSTORE"))
+ command << ' ' << "HIGHESTMODSEQ";
+
+ command << ")";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() != NULL)
+ {
+ const IMAPParser::response_data* 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");
+
+ m_connection->send(true, "NOOP", true);
+
+ std::auto_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 <int> IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid)
+{
+ std::vector<int> v;
+
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ command << "SEARCH UID " << uid << ":*";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_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");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = resp->continue_req_or_response_data();
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ {
+ throw exceptions::command_error("SEARCH",
+ resp->getErrorLog(), "invalid response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ (*it)->response_data()->mailbox_data();
+
+ // We are only interested in responses of type "SEARCH"
+ if (mailboxData == NULL ||
+ mailboxData->type() != IMAPParser::mailbox_data::SEARCH)
+ {
+ continue;
+ }
+
+ for (std::vector <IMAPParser::nz_number*>::const_iterator
+ it = mailboxData->search_nz_number_list().begin() ;
+ it != mailboxData->search_nz_number_list().end();
+ ++it)
+ {
+ v.push_back((*it)->value());
+ }
+ }
+
+ processStatusUpdate(resp.get());
+
+ return v;
+}
+
+
+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())
+ {
+ const IMAPParser::resp_text_code* code =
+ resp->response_done()->response_tagged()
+ ->resp_cond_state()->resp_text()->resp_text_code();
+
+ m_status->updateFromResponse(code);
+ }
+
+ // Process untagged responses
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ 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())
+ {
+ const IMAPParser::resp_text_code* code =
+ (*it)->response_data()->resp_cond_state()->resp_text()->resp_text_code();
+
+ m_status->updateFromResponse(code);
+ }
+ else if ((*it)->response_data() && (*it)->response_data()->mailbox_data())
+ {
+ m_status->updateFromResponse((*it)->response_data()->mailbox_data());
+ }
+ else if ((*it)->response_data() && (*it)->response_data()->message_data())
+ {
+ const IMAPParser::message_data* msgData = (*it)->response_data()->message_data();
+ const int msgNumber = msgData->number();
+
+ if ((*it)->response_data()->message_data()->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 <int>(1, msgNumber)));
+ }
+ else if ((*it)->response_data()->message_data()->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 <int>(1, msgNumber)));
+
+ expungedMessageCount++;
+ }
+ }
+ }
+
+ // New messages arrived
+ if (m_status->getMessageCount() > oldStatus->getMessageCount() - expungedMessageCount)
+ {
+ std::vector <int> newMessageNumbers;
+
+ for (int 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/src/vmime/net/imap/IMAPFolder.hpp b/src/vmime/net/imap/IMAPFolder.hpp
new file mode 100644
index 00000000..cc7334ff
--- /dev/null
+++ b/src/vmime/net/imap/IMAPFolder.hpp
@@ -0,0 +1,204 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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, shared_ptr <IMAPStore> store, const int type = TYPE_UNDEFINED, const int flags = FLAG_UNDEFINED);
+
+ ~IMAPFolder();
+
+ int getMode() const;
+
+ int getType();
+
+ int getFlags();
+
+ 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 int type);
+
+ bool exists();
+
+ void destroy();
+
+ bool isOpen() const;
+
+ shared_ptr <message> getMessage(const int num);
+ std::vector <shared_ptr <message> > getMessages(const messageSet& msgs);
+
+ std::vector <int> getMessageNumbersStartingOnUID(const message::uid& uid);
+
+ int 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);
+
+ void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
+
+ void status(int& count, int& 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(shared_ptr <message> msg, const fetchAttributes& options);
+
+ 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;
+
+ int m_type;
+ int m_flags;
+
+ 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/src/vmime/net/imap/IMAPFolderStatus.cpp b/src/vmime/net/imap/IMAPFolderStatus.cpp
new file mode 100644
index 00000000..c78a40f3
--- /dev/null
+++ b/src/vmime/net/imap/IMAPFolderStatus.cpp
@@ -0,0 +1,307 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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)
+{
+}
+
+
+unsigned int IMAPFolderStatus::getMessageCount() const
+{
+ return m_count;
+}
+
+
+unsigned int IMAPFolderStatus::getUnseenCount() const
+{
+ return m_unseen;
+}
+
+
+unsigned int 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)
+ {
+ const IMAPParser::status_att_list* statusAttList = resp->status_att_list();
+
+ for (std::vector <IMAPParser::status_att_val*>::const_iterator
+ jt = statusAttList->values().begin() ; jt != statusAttList->values().end() ; ++jt)
+ {
+ switch ((*jt)->type())
+ {
+ case IMAPParser::status_att_val::MESSAGES:
+ {
+ const unsigned int count =
+ static_cast <unsigned int>((*jt)->value_as_number()->value());
+
+ if (m_count != count)
+ {
+ m_count = count;
+ changed = true;
+ }
+
+ break;
+ }
+ case IMAPParser::status_att_val::UNSEEN:
+ {
+ const unsigned int unseen =
+ static_cast <unsigned int>((*jt)->value_as_number()->value());
+
+ if (m_unseen != unseen)
+ {
+ m_unseen = unseen;
+ changed = true;
+ }
+
+ break;
+ }
+ case IMAPParser::status_att_val::RECENT:
+ {
+ const unsigned int recent =
+ static_cast <unsigned int>((*jt)->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>((*jt)->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>((*jt)->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>((*jt)->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 unsigned int count =
+ static_cast <unsigned int>(resp->number()->value());
+
+ if (m_count != count)
+ {
+ m_count = count;
+ changed = true;
+ }
+ }
+ else if (resp->type() == IMAPParser::mailbox_data::RECENT)
+ {
+ const unsigned int recent =
+ static_cast <unsigned int>(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 unsigned int unseen =
+ static_cast <unsigned int>(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/src/vmime/net/imap/IMAPFolderStatus.hpp b/src/vmime/net/imap/IMAPFolderStatus.hpp
new file mode 100644
index 00000000..03ca5937
--- /dev/null
+++ b/src/vmime/net/imap/IMAPFolderStatus.hpp
@@ -0,0 +1,124 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ unsigned int getMessageCount() const;
+ unsigned int getUnseenCount() const;
+
+ shared_ptr <folderStatus> clone() const;
+
+ /** Returns the the number of messages with the Recent flag set.
+ *
+ * @return number of messages flagged Recent
+ */
+ unsigned int 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:
+
+ unsigned int m_count;
+ unsigned int m_unseen;
+ unsigned int 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/src/vmime/net/imap/IMAPMessage.cpp b/src/vmime/net/imap/IMAPMessage.cpp
new file mode 100644
index 00000000..c11aafc2
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessage.cpp
@@ -0,0 +1,649 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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_os(os), m_progress(progress)
+ {
+ }
+
+ 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 new targetStream(m_progress, m_os);
+ }
+ }
+
+ return (NULL);
+ }
+
+private:
+
+ utility::outputStream& m_os;
+ utility::progressListener* m_progress;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPMessage
+//
+
+
+IMAPMessage::IMAPMessage(shared_ptr <IMAPFolder> folder, const int 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(shared_ptr <IMAPFolder> folder, const int 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()
+{
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ if (folder)
+ folder->unregisterMessage(this);
+}
+
+
+void IMAPMessage::onFolderClosed()
+{
+ m_folder.reset();
+}
+
+
+int 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
+ (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(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(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());
+ }
+}
+
+
+void IMAPMessage::extractImpl
+ (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);
+
+ // 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 <int> 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 <int>::reverse_iterator it = numbers.rbegin() ; it != numbers.rend() ; ++it)
+ {
+ if (it != numbers.rbegin()) section << ".";
+ section << (*it + 1);
+ }
+ }
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ if (m_uid.empty())
+ command << "FETCH " << m_num << " BODY";
+ else
+ command << "UID FETCH " << m_uid << " BODY";
+
+ /*
+ BODY[] header + body
+ BODY.PEEK[] header + body (peek)
+ BODY[HEADER] header
+ BODY.PEEK[HEADER] header (peek)
+ BODY[TEXT] body
+ BODY.PEEK[TEXT] body (peek)
+ */
+
+ if (extractFlags & EXTRACT_PEEK)
+ command << ".PEEK";
+
+ command << "[";
+
+ if (section.str().empty())
+ {
+ // header + body
+ if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY))
+ command << "";
+ // body only
+ else if (extractFlags & EXTRACT_BODY)
+ command << "TEXT";
+ // header only
+ else if (extractFlags & EXTRACT_HEADER)
+ command << "HEADER";
+ }
+ else
+ {
+ command << section.str();
+
+ // header + body
+ if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY))
+ throw exceptions::operation_not_supported();
+ // body only
+ else if (extractFlags & EXTRACT_BODY)
+ command << ".TEXT";
+ // header only
+ else if (extractFlags & EXTRACT_HEADER)
+ command << ".MIME"; // "MIME" not "HEADER" for parts
+ }
+
+ command << "]";
+
+ if (start != 0 || length != static_cast <size_t>(-1))
+ command << "<" << start << "." << length << ">";
+
+ // Send the request
+ constCast <IMAPFolder>(folder)->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ std::auto_ptr <IMAPParser::response> resp
+ (constCast <IMAPFolder>(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)
+ }
+}
+
+
+int IMAPMessage::processFetchResponse
+ (const fetchAttributes& options, const IMAPParser::message_data* msgData)
+{
+ shared_ptr <IMAPFolder> folder = m_folder.lock();
+
+ // Get message attributes
+ const std::vector <IMAPParser::msg_att_item*> atts = msgData->msg_att()->items();
+ int changes = 0;
+
+ for (std::vector <IMAPParser::msg_att_item*>::const_iterator
+ it = atts.begin() ; it != atts.end() ; ++it)
+ {
+ switch ((*it)->type())
+ {
+ case IMAPParser::msg_att_item::FLAGS:
+ {
+ int flags = IMAPUtils::messageFlagsFromFlags((*it)->flag_list());
+
+ if (m_flags != flags)
+ {
+ m_flags = flags;
+ changes |= events::messageChangedEvent::TYPE_FLAGS;
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::UID:
+ {
+ m_uid = (*it)->unique_id()->value();
+ break;
+ }
+ case IMAPParser::msg_att_item::MODSEQ:
+ {
+ m_modseq = (*it)->mod_sequence_value()->value();
+ break;
+ }
+ case IMAPParser::msg_att_item::ENVELOPE:
+ {
+ if (!options.has(fetchAttributes::FULL_HEADER))
+ {
+ const IMAPParser::envelope* env = (*it)->envelope();
+ 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);
+
+ // Bcc
+ mailboxList bcc;
+ IMAPUtils::convertAddressList(*(env->env_bcc()), bcc);
+
+ if (!bcc.isEmpty())
+ hdr->Bcc()->setValue(bcc);
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_STRUCTURE:
+ {
+ m_structure = make_shared <IMAPMessageStructure>((*it)->body());
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_HEADER:
+ {
+ getOrCreateHeader()->parse((*it)->nstring()->value());
+ break;
+ }
+ case IMAPParser::msg_att_item::RFC822_SIZE:
+ {
+ m_size = static_cast <size_t>((*it)->number()->value());
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_SECTION:
+ {
+ if (!options.has(fetchAttributes::FULL_HEADER))
+ {
+ if ((*it)->section()->section_text1() &&
+ (*it)->section()->section_text1()->type()
+ == IMAPParser::section_text::HEADER_FIELDS)
+ {
+ header tempHeader;
+ tempHeader.parse((*it)->nstring()->value());
+
+ vmime::header& hdr = *getOrCreateHeader();
+ std::vector <shared_ptr <headerField> > fields = tempHeader.getFieldList();
+
+ for (std::vector <shared_ptr <headerField> >::const_iterator jt = fields.begin() ;
+ jt != fields.end() ; ++jt)
+ {
+ hdr.appendField(vmime::clone(*jt));
+ }
+ }
+ }
+
+ 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
+ (shared_ptr <bodyPart> parentPart, 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 int 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/src/vmime/net/imap/IMAPMessage.hpp b/src/vmime/net/imap/IMAPMessage.hpp
new file mode 100644
index 00000000..92903d69
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessage.hpp
@@ -0,0 +1,191 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPFolder> folder, const int num);
+ IMAPMessage(shared_ptr <IMAPFolder> folder, const int num, const uid& uid);
+
+ ~IMAPMessage();
+
+ int 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
+ (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(shared_ptr <messagePart> p);
+
+ shared_ptr <vmime::message> getParsedMessage();
+
+private:
+
+ /** Renumbers the message.
+ *
+ * @param number new sequence number
+ */
+ void renumber(const int 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(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(shared_ptr <bodyPart> parentPart, shared_ptr <messageStructure> str, int level = 0);
+
+
+ enum ExtractFlags
+ {
+ EXTRACT_HEADER = 0x1,
+ EXTRACT_BODY = 0x2,
+ EXTRACT_PEEK = 0x10
+ };
+
+ void extractImpl
+ (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;
+
+ int 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/src/vmime/net/imap/IMAPMessagePart.cpp b/src/vmime/net/imap/IMAPMessagePart.cpp
new file mode 100644
index 00000000..eed885dc
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessagePart.cpp
@@ -0,0 +1,161 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPMessagePart> parent, const int 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());
+}
+
+
+IMAPMessagePart::IMAPMessagePart(shared_ptr <IMAPMessagePart> parent, const int 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();
+ }
+ 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_structure = null;
+}
+
+
+shared_ptr <const messageStructure> IMAPMessagePart::getStructure() const
+{
+ if (m_structure != NULL)
+ return m_structure;
+ else
+ return IMAPMessageStructure::emptyStructure();
+}
+
+
+shared_ptr <messageStructure> IMAPMessagePart::getStructure()
+{
+ if (m_structure != NULL)
+ 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;
+}
+
+
+size_t IMAPMessagePart::getSize() const
+{
+ return m_size;
+}
+
+
+int IMAPMessagePart::getNumber() const
+{
+ return m_number;
+}
+
+
+shared_ptr <const header> IMAPMessagePart::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+ else
+ return m_header;
+}
+
+
+// static
+shared_ptr <IMAPMessagePart> IMAPMessagePart::create
+ (shared_ptr <IMAPMessagePart> parent, const int number, const IMAPParser::body* body)
+{
+ if (body->body_type_mpart())
+ {
+ shared_ptr <IMAPMessagePart> part = make_shared <IMAPMessagePart>(parent, number, body->body_type_mpart());
+ 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());
+ }
+}
+
+
+header& IMAPMessagePart::getOrCreateHeader()
+{
+ if (m_header != NULL)
+ 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/src/vmime/net/imap/IMAPMessagePart.hpp b/src/vmime/net/imap/IMAPMessagePart.hpp
new file mode 100644
index 00000000..af8581d7
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessagePart.hpp
@@ -0,0 +1,92 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPMessagePart> parent, const int number, const IMAPParser::body_type_mpart* mpart);
+ IMAPMessagePart(shared_ptr <IMAPMessagePart> parent, const int 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;
+ size_t getSize() const;
+ int getNumber() const;
+
+ shared_ptr <const header> getHeader() const;
+
+
+ static shared_ptr <IMAPMessagePart> create
+ (shared_ptr <IMAPMessagePart> parent, const int number, const IMAPParser::body* body);
+
+
+ header& getOrCreateHeader();
+
+private:
+
+ shared_ptr <IMAPMessageStructure> m_structure;
+ weak_ptr <IMAPMessagePart> m_parent;
+ shared_ptr <header> m_header;
+
+ int m_number;
+ size_t m_size;
+ mediaType m_mediaType;
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+
+#endif // VMIME_NET_IMAP_IMAPMESSAGEPART_HPP_INCLUDED
+
diff --git a/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp b/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp
new file mode 100644
index 00000000..1f53f082
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp
@@ -0,0 +1,216 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/inputStreamStringProxyAdapter.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPMessagePartContentHandler::IMAPMessagePartContentHandler
+ (shared_ptr <IMAPMessage> msg, 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>
+ (constCast <IMAPMessage>(m_message.lock()),
+ constCast <messagePart>(m_part.lock()),
+ m_encoding);
+}
+
+
+void IMAPMessagePartContentHandler::generate
+ (utility::outputStream& os, const vmime::encoding& enc, const size_t maxLineLength) const
+{
+ shared_ptr <IMAPMessage> msg = constCast <IMAPMessage>(m_message.lock());
+ shared_ptr <messagePart> part = constCast <messagePart>(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::inputStreamStringProxyAdapter 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 = constCast <IMAPMessage>(m_message.lock());
+ shared_ptr <messagePart> part = constCast <messagePart>(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 = constCast <IMAPMessage>(m_message.lock());
+ shared_ptr <messagePart> part = constCast <messagePart>(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/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp b/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp
new file mode 100644
index 00000000..cb52b2e3
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessagePartContentHandler.hpp
@@ -0,0 +1,87 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPMessage> msg, 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/src/vmime/net/imap/IMAPMessageStructure.cpp b/src/vmime/net/imap/IMAPMessageStructure.cpp
new file mode 100644
index 00000000..8dc333e9
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessageStructure.cpp
@@ -0,0 +1,94 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPMessagePart> parent, const std::vector <IMAPParser::body*>& list)
+{
+ int number = 0;
+
+ for (std::vector <IMAPParser::body*>::const_iterator
+ it = list.begin() ; it != list.end() ; ++it, ++number)
+ {
+ m_parts.push_back(IMAPMessagePart::create(parent, number, *it));
+ }
+}
+
+
+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/src/vmime/net/imap/IMAPMessageStructure.hpp b/src/vmime/net/imap/IMAPMessageStructure.hpp
new file mode 100644
index 00000000..44b6d6f0
--- /dev/null
+++ b/src/vmime/net/imap/IMAPMessageStructure.hpp
@@ -0,0 +1,75 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <IMAPMessagePart> parent, const std::vector <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/src/vmime/net/imap/IMAPParser.hpp b/src/vmime/net/imap/IMAPParser.hpp
new file mode 100644
index 00000000..8c7fcb60
--- /dev/null
+++ b/src/vmime/net/imap/IMAPParser.hpp
@@ -0,0 +1,5617 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/imap/IMAPTag.hpp"
+
+#include <vector>
+#include <stdexcept>
+#include <memory>
+
+
+//#define DEBUG_RESPONSE 1
+
+
+#if DEBUG_RESPONSE
+# include <iostream>
+#endif
+
+
+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)
+ : m_name(name), m_line(line), m_pos(currentPos)
+ {
+ ++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 << (std::uncaught_exception() ? "FALSE" : "TRUE") << ", 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;
+ };
+
+
+ #define DEBUG_ENTER_COMPONENT(x) \
+ IMAPParserDebugResponse dbg(x, line, *currentPos)
+
+ #define DEBUG_FOUND(x, y) \
+ std::cout << "FOUND: " << x << ": " << y << std::endl;
+#else
+ #define DEBUG_ENTER_COMPONENT(x)
+ #define DEBUG_FOUND(x, y)
+#endif
+
+
+class VMIME_EXPORT IMAPParser : public object
+{
+public:
+
+ IMAPParser(weak_ptr <IMAPTag> tag, weak_ptr <socket> sok, weak_ptr <timeoutHandler> _timeoutHandler)
+ : m_tag(tag), m_socket(sok), m_progress(NULL), m_strict(false),
+ m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler)
+ {
+ }
+
+
+ shared_ptr <const IMAPTag> getTag() const
+ {
+ return m_tag.lock();
+ }
+
+ void setSocket(shared_ptr <socket> sok)
+ {
+ m_socket = sok;
+ }
+
+ /** 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;
+
+ 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) { }
+
+ const vmime::string& string() const { return (m_string); }
+ vmime::string& string() { return (m_string); }
+
+
+ void putData(const vmime::string& chunk)
+ {
+ m_string += chunk;
+ }
+
+ private:
+
+ vmime::string& m_string;
+ };
+
+
+ // Target: redirect to an output stream
+ class targetStream : public target
+ {
+ public:
+
+ targetStream(utility::progressListener* progress, utility::outputStream& stream)
+ : target(progress), m_stream(stream) { }
+
+ 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());
+ }
+
+ private:
+
+ utility::outputStream& m_stream;
+ };
+
+
+ // 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 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 void go(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 \
+ { \
+ void go(IMAPParser& parser, string& line, size_t* currentPos) \
+ { \
+ DEBUG_ENTER_COMPONENT(#name); \
+ parent::go(parser, line, currentPos); \
+ } \
+ }
+
+
+ //
+ // Parse one character
+ //
+
+ template <char C>
+ class one_char : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'");
+
+ const size_t pos = *currentPos;
+
+ if (pos < line.length() && line[pos] == C)
+ *currentPos = pos + 1;
+ else
+ throw exceptions::invalid_response("", makeResponseLine("", line, pos));
+ }
+ };
+
+
+ //
+ // SPACE ::= <ASCII SP, space, 0x20>
+ //
+
+ class SPACE : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("SPACE");
+
+ size_t pos = *currentPos;
+
+ while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t'))
+ ++pos;
+
+ if (pos > *currentPos)
+ *currentPos = pos;
+ else
+ throw exceptions::invalid_response("", makeResponseLine("SPACE", line, pos));
+ }
+ };
+
+
+ //
+ // CR ::= <ASCII CR, carriage return, 0x0D>
+ // LF ::= <ASCII LF, line feed, 0x0A>
+ // CRLF ::= CR LF
+ //
+
+ class CRLF : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("CRLF");
+
+ size_t pos = *currentPos;
+
+ parser.check <SPACE>(line, &pos, true);
+
+ if (pos + 1 < line.length() &&
+ line[pos] == 0x0d && line[pos + 1] == 0x0a)
+ {
+ *currentPos = pos + 2;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("CRLF", line, pos));
+ }
+ }
+ };
+
+
+ //
+ // 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")
+ //
+
+ class xtag : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("tag");
+
+ size_t pos = *currentPos;
+
+ bool end = false;
+
+ string tagString;
+ 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;
+ }
+ }
+
+ if (tagString == string(*parser.getTag()))
+ {
+ *currentPos = pos;
+ }
+ else
+ {
+ // Invalid tag
+ throw exceptions::invalid_response("", makeResponseLine("tag", line, pos));
+ }
+ }
+ };
+
+
+ //
+ // 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)
+ //
+
+ class number : public component
+ {
+ public:
+
+ number(const bool nonZero = false)
+ : m_nonZero(nonZero), m_value(0)
+ {
+ }
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("number");
+
+ 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)
+ {
+ m_value = val;
+ *currentPos = pos;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("number", line, pos));
+ }
+ }
+
+ private:
+
+ const bool m_nonZero;
+ unsigned long m_value;
+
+ public:
+
+ unsigned long value() const { return (m_value); }
+ };
+
+
+ // 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)
+ {
+ }
+ };
+
+
+ //
+ // text ::= 1*TEXT_CHAR
+ //
+ // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f>
+ // TEXT_CHAR ::= <any CHAR except CR and LF>
+ //
+
+ class text : public component
+ {
+ public:
+
+ text(bool allow8bits = false, const char except = 0)
+ : m_allow8bits(allow8bits), m_except(except)
+ {
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("text");
+
+ 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)
+ {
+ m_value.resize(len);
+ std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin());
+
+ *currentPos = pos;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("text", line, pos));
+ }
+ }
+
+ private:
+
+ string m_value;
+ const bool m_allow8bits;
+ const char m_except;
+
+ public:
+
+ const string& value() const { return (m_value); }
+ };
+
+
+ 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>
+ //
+
+ class QUOTED_CHAR : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("quoted_char");
+
+ 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
+ {
+ m_value = c;
+ *currentPos = pos + 1;
+ }
+ else if (c == '\\' && pos + 1 < line.length() &&
+ (line[pos + 1] == '"' || line[pos + 1] == '\\'))
+ {
+ m_value = line[pos + 1];
+ *currentPos = pos + 2;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("QUOTED_CHAR", line, pos));
+ }
+ }
+
+ private:
+
+ char m_value;
+
+ public:
+
+ char value() const { return (m_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>
+ //
+
+ class quoted_text : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("quoted_text");
+
+ size_t pos = *currentPos;
+ size_t len = 0;
+ bool valid = false;
+
+ m_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 == '\\')
+ m_value += c;
+ else
+ {
+ m_value += '\\';
+ m_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
+ {
+ m_value += c;
+
+ ++pos;
+ ++len;
+ }
+ else
+ {
+ valid = false;
+ end = true;
+ }
+ }
+ }
+
+ if (valid)
+ {
+ *currentPos = pos;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("quoted_text", line, pos));
+ }
+ }
+
+ private:
+
+ string m_value;
+
+ public:
+
+ const string& value() const { return (m_value); }
+ };
+
+
+ //
+ // nil ::= "NIL"
+ //
+
+ class NIL : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("NIL");
+
+ size_t pos = *currentPos;
+
+ parser.checkWithArg <special_atom>(line, &pos, "nil");
+
+ *currentPos = pos;
+ }
+ };
+
+
+ //
+ // 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>
+ //
+
+ class xstring : public component
+ {
+ public:
+
+ xstring(const bool canBeNIL = false, component* comp = NULL, const int data = 0)
+ : m_canBeNIL(canBeNIL), m_component(comp), m_data(data)
+ {
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("string");
+
+ size_t pos = *currentPos;
+
+ if (m_canBeNIL &&
+ parser.checkWithArg <special_atom>(line, &pos, "nil", true))
+ {
+ // NIL
+ }
+ else
+ {
+ pos = *currentPos;
+
+ // quoted ::= <"> *QUOTED_CHAR <">
+ if (parser.check <one_char <'"'> >(line, &pos, true))
+ {
+ std::auto_ptr <quoted_text> text(parser.get <quoted_text>(line, &pos));
+ parser.check <one_char <'"'> >(line, &pos);
+
+ if (parser.m_literalHandler != NULL)
+ {
+ literalHandler::target* target =
+ parser.m_literalHandler->targetFor(*m_component, m_data);
+
+ if (target != NULL)
+ {
+ m_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);
+ }
+
+ delete (target);
+ }
+ else
+ {
+ m_value = text->value();
+ }
+ }
+ else
+ {
+ m_value = text->value();
+ }
+
+ DEBUG_FOUND("string[quoted]", "<length=" << m_value.length() << ", value='" << m_value << "'>");
+ }
+ // literal ::= "{" number "}" CRLF *CHAR8
+ else
+ {
+ parser.check <one_char <'{'> >(line, &pos);
+
+ number* num = parser.get <number>(line, &pos);
+
+ const size_t length = num->value();
+ delete (num);
+
+ parser.check <one_char <'}'> >(line, &pos);
+
+ parser.check <CRLF>(line, &pos);
+
+
+ if (parser.m_literalHandler != NULL)
+ {
+ literalHandler::target* target =
+ parser.m_literalHandler->targetFor(*m_component, m_data);
+
+ if (target != NULL)
+ {
+ m_value = "[literal-handler]";
+
+ parser.m_progress = target->progressListener();
+ parser.readLiteral(*target, length);
+ parser.m_progress = NULL;
+
+ delete (target);
+ }
+ else
+ {
+ literalHandler::targetString target(NULL, m_value);
+ parser.readLiteral(target, length);
+ }
+ }
+ else
+ {
+ literalHandler::targetString target(NULL, m_value);
+ parser.readLiteral(target, length);
+ }
+
+ line += parser.readLine();
+
+ DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << m_value << "'>");
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ bool m_canBeNIL;
+ string m_value;
+
+ component* m_component;
+ const int m_data;
+
+ public:
+
+ const string& value() const { return (m_value); }
+ void setValue(const string& val) { m_value = val; }
+ };
+
+
+ //
+ // nstring ::= string / nil
+ //
+
+ class nstring : public xstring
+ {
+ public:
+
+ nstring(component* comp = NULL, const int data = 0)
+ : xstring(true, comp, data)
+ {
+ }
+ };
+
+
+ //
+ // astring ::= atom / string
+ //
+
+ class astring : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("astring");
+
+ size_t pos = *currentPos;
+
+ xstring* str = NULL;
+
+ if ((str = parser.get <xstring>(line, &pos, true)))
+ {
+ m_value = str->value();
+ delete (str);
+ }
+ else
+ {
+ atom* at = parser.get <atom>(line, &pos);
+ m_value = at->value();
+ delete (at);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ string m_value;
+
+ public:
+
+ const string& value() const { return (m_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>
+ //
+
+ class atom : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("atom");
+
+ 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)
+ {
+ m_value.resize(len);
+ std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin());
+
+ *currentPos = pos;
+ }
+ else
+ {
+ throw exceptions::invalid_response("", makeResponseLine("atom", line, pos));
+ }
+ }
+
+ private:
+
+ string m_value;
+
+ public:
+
+ const string& value() const { return (m_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:
+
+ special_atom(const char* str)
+ : m_string(str) // 'string' must be in lower-case
+ {
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT(string("special_atom(") + m_string + ")");
+
+ size_t pos = *currentPos;
+
+ atom::go(parser, line, &pos);
+
+ 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)
+ {
+ throw exceptions::invalid_response("", makeResponseLine(string("special_atom <") + m_string + ">", line, pos));
+ }
+ else
+ {
+ *currentPos = pos;
+ }
+ }
+
+ private:
+
+ const char* m_string;
+ };
+
+
+ //
+ // text_mime2 ::= "=?" <charset> "?" <encoding> "?" <encoded-text> "?="
+ // ;; Syntax defined in [MIME-HDRS]
+ //
+
+ class text_mime2 : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("text_mime2");
+
+ size_t pos = *currentPos;
+
+ atom* theCharset = NULL, *theEncoding = NULL;
+ text* theText = NULL;
+
+ try
+ {
+ parser.check <one_char <'='> >(line, &pos);
+ parser.check <one_char <'?'> >(line, &pos);
+
+ theCharset = parser.get <atom>(line, &pos);
+
+ parser.check <one_char <'?'> >(line, &pos);
+
+ theEncoding = parser.get <atom>(line, &pos);
+
+ parser.check <one_char <'?'> >(line, &pos);
+
+ theText = parser.get <text8_except <'?'> >(line, &pos);
+
+ parser.check <one_char <'?'> >(line, &pos);
+ parser.check <one_char <'='> >(line, &pos);
+ }
+ catch (std::exception&)
+ {
+ delete (theCharset);
+ delete (theEncoding);
+ delete (theText);
+
+ throw;
+ }
+
+ m_charset = theCharset->value();
+ delete (theCharset);
+
+ // Decode text
+ utility::encoder::encoder* theEncoder = NULL;
+
+ if (theEncoding->value()[0] == 'q' || theEncoding->value()[0] == 'Q')
+ {
+ // Quoted-printable
+ theEncoder = new utility::encoder::qpEncoder();
+ theEncoder->getProperties()["rfc2047"] = true;
+ }
+ else if (theEncoding->value()[0] == 'b' || theEncoding->value()[0] == 'B')
+ {
+ // Base64
+ theEncoder = new utility::encoder::b64Encoder();
+ }
+
+ if (theEncoder)
+ {
+ utility::inputStreamStringAdapter in(theText->value());
+ utility::outputStreamStringAdapter out(m_value);
+
+ theEncoder->decode(in, out);
+ delete (theEncoder);
+ }
+ // No decoder available
+ else
+ {
+ m_value = theText->value();
+ }
+
+ delete (theEncoding);
+ delete (theText);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ vmime::charset m_charset;
+ string m_value;
+
+ public:
+
+ const vmime::charset& charset() const { return (m_charset); }
+ const string& value() const { return (m_value); }
+ };
+
+
+ // seq-number = nz-number / "*"
+ // ; message sequence number (COPY, FETCH, STORE
+ // ; commands) or unique identifier (UID COPY,
+ // ; UID FETCH, UID STORE commands).
+
+ class seq_number : public component
+ {
+ public:
+
+ seq_number()
+ : m_number(NULL), m_star(false)
+ {
+ }
+
+ ~seq_number()
+ {
+ delete m_number;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("seq_number");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'*'> >(line, &pos, true))
+ {
+ m_star = true;
+ m_number = NULL;
+ }
+ else
+ {
+ m_star = false;
+ m_number = parser.get <IMAPParser::number>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::number* m_number;
+ bool m_star;
+
+ public:
+
+ const IMAPParser::number* number() const { return m_number; }
+ bool star() const { return m_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.
+
+ class seq_range : public component
+ {
+ public:
+
+ seq_range()
+ : m_first(NULL), m_last(NULL)
+ {
+ }
+
+ ~seq_range()
+ {
+ delete m_first;
+ delete m_last;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("seq_range");
+
+ size_t pos = *currentPos;
+
+ m_first = parser.get <seq_number>(line, &pos);
+
+ parser.check <one_char <'*'> >(line, &pos);
+
+ m_last = parser.get <seq_number>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::seq_number* m_first;
+ IMAPParser::seq_number* m_last;
+
+ public:
+
+ const IMAPParser::seq_number* first() const { return m_first; }
+ const IMAPParser::seq_number* last() const { return m_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
+
+ class sequence_set : public component
+ {
+ public:
+
+ sequence_set()
+ : m_number(NULL), m_range(NULL), m_nextSet(NULL)
+ {
+ }
+
+ ~sequence_set()
+ {
+ delete m_number;
+ delete m_range;
+ delete m_nextSet;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("sequence_set");
+
+ size_t pos = *currentPos;
+
+ if ((m_range = parser.get <IMAPParser::seq_range>(line, &pos, true)) == NULL)
+ m_number = parser.get <IMAPParser::seq_number>(line, &pos);
+
+ if (parser.check <one_char <','> >(line, &pos, true))
+ m_nextSet = parser.get <sequence_set>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::seq_number* m_number;
+ IMAPParser::seq_range* m_range;
+ IMAPParser::sequence_set* m_nextSet;
+
+ public:
+
+ const IMAPParser::seq_number* seq_number() const { return m_number; }
+ const IMAPParser::seq_range* seq_range() const { return m_range; }
+ const IMAPParser::sequence_set* next_sequence_set() const { return m_nextSet; }
+ };
+
+
+ // mod-sequence-value = 1*DIGIT
+ // ;; Positive unsigned 64-bit integer
+ // ;; (mod-sequence)
+ // ;; (1 <= n < 18,446,744,073,709,551,615)
+
+ class mod_sequence_value : public component
+ {
+ public:
+
+ mod_sequence_value()
+ : m_value(0)
+ {
+ }
+
+ void go(IMAPParser& /* parser */, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mod_sequence_value");
+
+ 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;
+ }
+ }
+
+ m_value = val;
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ vmime_uint64 m_value;
+
+ public:
+
+ vmime_uint64 value() const { return m_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
+ //
+
+ class flag : public component
+ {
+ public:
+
+ flag()
+ : m_type(UNKNOWN), m_flag_keyword(NULL)
+ {
+ }
+
+ ~flag()
+ {
+ delete (m_flag_keyword);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("flag_keyword");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'\\'> >(line, &pos, true))
+ {
+ if (parser.check <one_char <'*'> >(line, &pos, true))
+ {
+ m_type = STAR;
+ }
+ else
+ {
+ atom* at = parser.get <atom>(line, &pos);
+ const string name = utility::stringUtils::toLower(at->value());
+ delete (at);
+
+ if (name == "answered")
+ m_type = ANSWERED;
+ else if (name == "flagged")
+ m_type = FLAGGED;
+ else if (name == "deleted")
+ m_type = DELETED;
+ else if (name == "seen")
+ m_type = SEEN;
+ else if (name == "draft")
+ m_type = DRAFT;
+ else
+ {
+ m_type = UNKNOWN;
+ m_name = name;
+ }
+ }
+ }
+ else
+ {
+ m_type = KEYWORD_OR_EXTENSION;
+ m_flag_keyword = parser.get <atom>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ UNKNOWN,
+ ANSWERED,
+ FLAGGED,
+ DELETED,
+ SEEN,
+ DRAFT,
+ KEYWORD_OR_EXTENSION,
+ STAR // * = custom flags allowed
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ IMAPParser::atom* m_flag_keyword;
+
+ public:
+
+ Type type() const { return (m_type); }
+ const string& name() const { return (m_name); }
+
+ const IMAPParser::atom* flag_keyword() const { return (m_flag_keyword); }
+ };
+
+
+ //
+ // flag_list ::= "(" #flag ")"
+ //
+
+ class flag_list : public component
+ {
+ public:
+
+ ~flag_list()
+ {
+ for (std::vector <flag*>::iterator it = m_flags.begin() ;
+ it != m_flags.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("flag_list");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ m_flags.push_back(parser.get <flag>(line, &pos));
+ parser.check <SPACE>(line, &pos, true);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <flag*> m_flags;
+
+ public:
+
+ const std::vector <flag*>& flags() const { return (m_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.
+ //
+
+ class mailbox : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox");
+
+ size_t pos = *currentPos;
+
+ if (parser.checkWithArg <special_atom>(line, &pos, "inbox", true))
+ {
+ m_type = INBOX;
+ m_name = "INBOX";
+ }
+ else
+ {
+ m_type = OTHER;
+
+ astring* astr = parser.get <astring>(line, &pos);
+ m_name = astr->value();
+ delete (astr);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ INBOX,
+ OTHER
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ public:
+
+ Type type() const { return (m_type); }
+ const string& name() const { return (m_name); }
+ };
+
+
+ //
+ // mailbox_flag := "\Marked" / "\Noinferiors" /
+ // "\Noselect" / "\Unmarked" / flag_extension
+ //
+
+ class mailbox_flag : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_flag");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'\\'> >(line, &pos, true))
+ {
+ atom* at = parser.get <atom>(line, &pos);
+ const string name = utility::stringUtils::toLower(at->value());
+ delete (at);
+
+ if (name == "marked")
+ m_type = MARKED;
+ else if (name == "noinferiors")
+ m_type = NOINFERIORS;
+ else if (name == "noselect")
+ m_type = NOSELECT;
+ else if (name == "unmarked")
+ m_type = UNMARKED;
+ else
+ {
+ m_type = UNKNOWN;
+ m_name = "\\" + name;
+ }
+ }
+ else
+ {
+ atom* at = parser.get <atom>(line, &pos);
+ const string name = utility::stringUtils::toLower(at->value());
+ delete (at);
+
+ m_type = UNKNOWN;
+ m_name = name;
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ UNKNOWN,
+ MARKED,
+ NOINFERIORS,
+ NOSELECT,
+ UNMARKED
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ public:
+
+ Type type() const { return (m_type); }
+ const string& name() const { return (m_name); }
+ };
+
+
+ //
+ // mailbox_flag_list ::= "(" #(mailbox_flag) ")"
+ //
+
+ class mailbox_flag_list : public component
+ {
+ public:
+
+ ~mailbox_flag_list()
+ {
+ for (std::vector <mailbox_flag*>::iterator it = m_flags.begin() ;
+ it != m_flags.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_flag_list");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ m_flags.push_back(parser.get <mailbox_flag>(line, &pos));
+ parser.check <SPACE>(line, &pos, true);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <mailbox_flag*> m_flags;
+
+ public:
+
+ const std::vector <mailbox_flag*>& flags() const { return (m_flags); }
+ };
+
+
+ //
+ // mailbox_list ::= mailbox_flag_list SPACE
+ // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
+ //
+
+ class mailbox_list : public component
+ {
+ public:
+
+ mailbox_list()
+ : m_mailbox_flag_list(NULL),
+ m_mailbox(NULL), m_quoted_char('\0')
+ {
+ }
+
+ ~mailbox_list()
+ {
+ delete (m_mailbox_flag_list);
+ delete (m_mailbox);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_list");
+
+ size_t pos = *currentPos;
+
+ m_mailbox_flag_list = parser.get <IMAPParser::mailbox_flag_list>(line, &pos);
+
+ parser.check <SPACE>(line, &pos);
+
+ if (!parser.check <NIL>(line, &pos, true))
+ {
+ parser.check <one_char <'"'> >(line, &pos);
+
+ QUOTED_CHAR* qc = parser.get <QUOTED_CHAR>(line, &pos);
+ m_quoted_char = qc->value();
+ delete (qc);
+
+ parser.check <one_char <'"'> >(line, &pos);
+ }
+
+ parser.check <SPACE>(line, &pos);
+
+ m_mailbox = parser.get <IMAPParser::mailbox>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::mailbox_flag_list* m_mailbox_flag_list;
+ IMAPParser::mailbox* m_mailbox;
+ char m_quoted_char;
+
+ public:
+
+ const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); }
+ const IMAPParser::mailbox* mailbox() const { return (m_mailbox); }
+ char quoted_char() const { return (m_quoted_char); }
+ };
+
+
+ //
+ // auth_type ::= atom
+ // ;; Defined by [IMAP-AUTH]
+ //
+
+ class auth_type : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("auth_type");
+
+ atom* at = parser.get <atom>(line, currentPos);
+ m_name = utility::stringUtils::toLower(at->value());
+ delete (at);
+
+ if (m_name == "kerberos_v4")
+ m_type = KERBEROS_V4;
+ else if (m_name == "gssapi")
+ m_type = GSSAPI;
+ else if (m_name == "skey")
+ m_type = SKEY;
+ else
+ m_type = UNKNOWN;
+ }
+
+
+ enum Type
+ {
+ UNKNOWN,
+
+ // RFC 1731 - IMAP4 Authentication Mechanisms
+ KERBEROS_V4,
+ GSSAPI,
+ SKEY
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ public:
+
+ Type type() const { return (m_type); }
+ const string name() const { return (m_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
+ //
+
+ class status_att_val : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("status_att");
+
+ size_t pos = *currentPos;
+
+ // "HIGHESTMODSEQ" SP mod-sequence-valzer
+ if (parser.checkWithArg <special_atom>(line, &pos, "highestmodseq", true))
+ {
+ m_type = HIGHESTMODSEQ;
+
+ parser.check <SPACE>(line, &pos);
+ m_value = parser.get <IMAPParser::mod_sequence_value>(line, &pos);
+ }
+ else
+ {
+ if (parser.checkWithArg <special_atom>(line, &pos, "messages", true))
+ {
+ m_type = MESSAGES;
+ }
+ else if (parser.checkWithArg <special_atom>(line, &pos, "recent", true))
+ {
+ m_type = RECENT;
+ }
+ else if (parser.checkWithArg <special_atom>(line, &pos, "uidnext", true))
+ {
+ m_type = UIDNEXT;
+ }
+ else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true))
+ {
+ m_type = UIDVALIDITY;
+ }
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "unseen");
+ m_type = UNSEEN;
+ }
+
+ parser.check <SPACE>(line, &pos);
+ m_value = parser.get <IMAPParser::number>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ // Extensions
+ HIGHESTMODSEQ,
+
+ // Standard IMAP
+ MESSAGES,
+ RECENT,
+ UIDNEXT,
+ UIDVALIDITY,
+ UNSEEN
+ };
+
+ private:
+
+ Type m_type;
+ IMAPParser::component* m_value;
+
+ public:
+
+ Type type() const { return (m_type); }
+
+ const IMAPParser::number* value_as_number() const
+ {
+ return dynamic_cast <IMAPParser::number *>(m_value);
+ }
+
+ const IMAPParser::mod_sequence_value* value_as_mod_sequence_value() const
+ {
+ return dynamic_cast <IMAPParser::mod_sequence_value *>(m_value);
+ }
+ };
+
+
+ // status-att-list = status-att-val *(SP status-att-val)
+
+ class status_att_list : public component
+ {
+ public:
+
+ ~status_att_list()
+ {
+ for (std::vector <status_att_val*>::iterator it = m_values.begin() ;
+ it != m_values.end() ; ++it)
+ {
+ delete *it;
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("status_att_list");
+
+ size_t pos = *currentPos;
+
+ m_values.push_back(parser.get <IMAPParser::status_att_val>(line, &pos));
+
+ while (parser.check <SPACE>(line, &pos, true))
+ m_values.push_back(parser.get <IMAPParser::status_att_val>(line, &pos));
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <status_att_val*> m_values;
+
+ public:
+
+ const std::vector <status_att_val*>& values() const { return m_values; }
+ };
+
+
+ //
+ // capability ::= "AUTH=" auth_type / atom
+ // ;; New capabilities MUST begin with "X" or be
+ // ;; registered with IANA as standard or standards-track
+ //
+
+ class capability : public component
+ {
+ public:
+
+ capability()
+ : m_auth_type(NULL), m_atom(NULL)
+ {
+ }
+
+ ~capability()
+ {
+ delete (m_auth_type);
+ delete (m_atom);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("capability");
+
+ size_t pos = *currentPos;
+
+ class atom* at = parser.get <IMAPParser::atom>(line, &pos);
+
+ string value = at->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;
+ m_auth_type = parser.get <IMAPParser::auth_type>(value, &pos);
+ delete (at);
+ }
+ else
+ {
+ m_atom = at;
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::auth_type* m_auth_type;
+ IMAPParser::atom* m_atom;
+
+ public:
+
+ const IMAPParser::auth_type* auth_type() const { return (m_auth_type); }
+ const IMAPParser::atom* atom() const { return (m_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.
+ //
+
+ class capability_data : public component
+ {
+ public:
+
+ ~capability_data()
+ {
+ for (std::vector <capability*>::iterator it = m_capabilities.begin() ;
+ it != m_capabilities.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("capability_data");
+
+ size_t pos = *currentPos;
+
+ parser.checkWithArg <special_atom>(line, &pos, "capability");
+
+ while (parser.check <SPACE>(line, &pos, true))
+ {
+ capability* cap;
+
+ if (parser.isStrict() || m_capabilities.empty())
+ cap = parser.get <capability>(line, &pos);
+ else
+ cap = parser.get <capability>(line, &pos, /* noThrow */ true); // allow SPACE at end of line (Apple iCloud IMAP server)
+
+ if (cap == NULL) break;
+
+ m_capabilities.push_back(cap);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <capability*> m_capabilities;
+
+ public:
+
+ const std::vector <capability*>& capabilities() const { return (m_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 <">
+ //
+
+ class date_time : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("date_time");
+
+ size_t pos = *currentPos;
+
+ // <"> date_day_fixed "-" date_month "-" date_year
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.check <SPACE>(line, &pos, true);
+
+ std::auto_ptr <number> nd(parser.get <number>(line, &pos));
+
+ parser.check <one_char <'-'> >(line, &pos);
+
+ std::auto_ptr <atom> amo(parser.get <atom>(line, &pos));
+
+ parser.check <one_char <'-'> >(line, &pos);
+
+ std::auto_ptr <number> ny(parser.get <number>(line, &pos));
+
+ parser.check <SPACE>(line, &pos, true);
+
+ // 2digit ":" 2digit ":" 2digit
+ std::auto_ptr <number> nh(parser.get <number>(line, &pos));
+
+ parser.check <one_char <':'> >(line, &pos);
+
+ std::auto_ptr <number> nmi(parser.get <number>(line, &pos));
+
+ parser.check <one_char <':'> >(line, &pos);
+
+ std::auto_ptr <number> ns(parser.get <number>(line, &pos));
+
+ parser.check <SPACE>(line, &pos, true);
+
+ // ("+" / "-") 4digit
+ int sign = 1;
+
+ if (!(parser.check <one_char <'+'> >(line, &pos, true)))
+ parser.check <one_char <'-'> >(line, &pos);
+
+ std::auto_ptr <number> nz(parser.get <number>(line, &pos));
+
+ parser.check <one_char <'"'> >(line, &pos);
+
+
+ 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;
+ }
+
+ private:
+
+ vmime::datetime m_datetime;
+ };
+
+
+ //
+ // header_fld_name ::= astring
+ //
+
+ typedef astring header_fld_name;
+
+
+ //
+ // header_list ::= "(" 1#header_fld_name ")"
+ //
+
+ class header_list : public component
+ {
+ public:
+
+ ~header_list()
+ {
+ for (std::vector <header_fld_name*>::iterator it = m_fld_names.begin() ;
+ it != m_fld_names.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("header_list");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ m_fld_names.push_back(parser.get <header_fld_name>(line, &pos));
+ parser.check <SPACE>(line, &pos, true);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <header_fld_name*> m_fld_names;
+
+ public:
+
+ const std::vector <header_fld_name*>& fld_names() const { return (m_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.
+ //
+
+ class body_extension : public component
+ {
+ public:
+
+ body_extension()
+ : m_nstring(NULL), m_number(NULL)
+ {
+ }
+
+ ~body_extension()
+ {
+ delete (m_nstring);
+ delete (m_number);
+
+ for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ;
+ it != m_body_extensions.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'('> >(line, &pos, true))
+ {
+ m_body_extensions.push_back
+ (parser.get <body_extension>(line, &pos));
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ m_body_extensions.push_back(parser.get <body_extension>(line, &pos));
+ parser.check <SPACE>(line, &pos, true);
+ }
+ }
+ else
+ {
+ if (!(m_nstring = parser.get <IMAPParser::nstring>(line, &pos, true)))
+ m_number = parser.get <IMAPParser::number>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::nstring* m_nstring;
+ IMAPParser::number* m_number;
+
+ std::vector <body_extension*> m_body_extensions;
+
+ public:
+
+ IMAPParser::nstring* nstring() const { return (m_nstring); }
+ IMAPParser::number* number() const { return (m_number); }
+
+ const std::vector <body_extension*>& body_extensions() const { return (m_body_extensions); }
+ };
+
+
+ //
+ // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
+ // SPACE header_list / "TEXT" / "MIME"
+ //
+
+ class section_text : public component
+ {
+ public:
+
+ section_text()
+ : m_header_list(NULL)
+ {
+ }
+
+ ~section_text()
+ {
+ delete (m_header_list);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("section_text");
+
+ size_t pos = *currentPos;
+
+ // "HEADER.FIELDS" [".NOT"] SPACE header_list
+ const bool b1 = parser.checkWithArg <special_atom>(line, &pos, "header.fields.not", true);
+ const bool b2 = (b1 ? false : parser.checkWithArg <special_atom>(line, &pos, "header.fields", true));
+
+ if (b1 || b2)
+ {
+ m_type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS;
+
+ parser.check <SPACE>(line, &pos);
+ m_header_list = parser.get <IMAPParser::header_list>(line, &pos);
+ }
+ // "HEADER"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "header", true))
+ {
+ m_type = HEADER;
+ }
+ // "MIME"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "mime", true))
+ {
+ m_type = MIME;
+ }
+ // "TEXT"
+ else
+ {
+ m_type = TEXT;
+
+ parser.checkWithArg <special_atom>(line, &pos, "text");
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ HEADER,
+ HEADER_FIELDS,
+ HEADER_FIELDS_NOT,
+ MIME,
+ TEXT
+ };
+
+ private:
+
+ Type m_type;
+ IMAPParser::header_list* m_header_list;
+
+ public:
+
+ Type type() const { return (m_type); }
+ const IMAPParser::header_list* header_list() const { return (m_header_list); }
+ };
+
+
+ //
+ // section ::= "[" [section_text / (nz_number *["." nz_number]
+ // ["." (section_text / "MIME")])] "]"
+ //
+
+ class section : public component
+ {
+ public:
+
+ section()
+ : m_section_text1(NULL), m_section_text2(NULL)
+ {
+ }
+
+ ~section()
+ {
+ delete (m_section_text1);
+ delete (m_section_text2);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("section");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'['> >(line, &pos);
+
+ if (!parser.check <one_char <']'> >(line, &pos, true))
+ {
+ if (!(m_section_text1 = parser.get <section_text>(line, &pos, true)))
+ {
+ nz_number* num = parser.get <nz_number>(line, &pos);
+ m_nz_numbers.push_back(static_cast <unsigned int>(num->value()));
+ delete (num);
+
+ while (parser.check <one_char <'.'> >(line, &pos, true))
+ {
+ if ((num = parser.get <nz_number>(line, &pos, true)))
+ {
+ m_nz_numbers.push_back(static_cast <unsigned int>(num->value()));
+ delete (num);
+ }
+ else
+ {
+ m_section_text2 = parser.get <section_text>(line, &pos);
+ break;
+ }
+ }
+ }
+
+ parser.check <one_char <']'> >(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ section_text* m_section_text1;
+ section_text* m_section_text2;
+ std::vector <unsigned int> m_nz_numbers;
+
+ public:
+
+ const section_text* section_text1() const { return (m_section_text1); }
+ const section_text* section_text2() const { return (m_section_text2); }
+ const std::vector <unsigned int>& nz_numbers() const { return (m_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 ")"
+ //
+
+ class address : public component
+ {
+ public:
+
+ address()
+ : m_addr_name(NULL), m_addr_adl(NULL),
+ m_addr_mailbox(NULL), m_addr_host(NULL)
+ {
+ }
+
+ ~address()
+ {
+ delete (m_addr_name);
+ delete (m_addr_adl);
+ delete (m_addr_mailbox);
+ delete (m_addr_host);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("address");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+ m_addr_name = parser.get <nstring>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_addr_adl = parser.get <nstring>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_addr_mailbox = parser.get <nstring>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_addr_host = parser.get <nstring>(line, &pos);
+ parser.check <one_char <')'> >(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ nstring* m_addr_name;
+ nstring* m_addr_adl;
+ nstring* m_addr_mailbox;
+ nstring* m_addr_host;
+
+ public:
+
+ nstring* addr_name() const { return (m_addr_name); }
+ nstring* addr_adl() const { return (m_addr_adl); }
+ nstring* addr_mailbox() const { return (m_addr_mailbox); }
+ nstring* addr_host() const { return (m_addr_host); }
+ };
+
+
+ //
+ // address_list ::= "(" 1*address ")" / nil
+ //
+
+ class address_list : public component
+ {
+ public:
+
+ ~address_list()
+ {
+ for (std::vector <address*>::iterator it = m_addresses.begin() ;
+ it != m_addresses.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("address_list");
+
+ size_t pos = *currentPos;
+
+ if (!parser.check <NIL>(line, &pos, true))
+ {
+ parser.check <one_char <'('> >(line, &pos);
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ m_addresses.push_back(parser.get <address>(line, &pos));
+ parser.check <SPACE>(line, &pos, true);
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <address*> m_addresses;
+
+ public:
+
+ const std::vector <address*>& addresses() const { return (m_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 ")"
+ //
+
+ class envelope : public component
+ {
+ public:
+
+ envelope()
+ : m_env_date(NULL), m_env_subject(NULL),
+ m_env_from(NULL), m_env_sender(NULL), m_env_reply_to(NULL),
+ m_env_to(NULL), m_env_cc(NULL), m_env_bcc(NULL),
+ m_env_in_reply_to(NULL), m_env_message_id(NULL)
+ {
+ }
+
+ ~envelope()
+ {
+ delete (m_env_date);
+ delete (m_env_subject);
+ delete (m_env_from);
+ delete (m_env_sender);
+ delete (m_env_reply_to);
+ delete (m_env_to);
+ delete (m_env_cc);
+ delete (m_env_bcc);
+ delete (m_env_in_reply_to);
+ delete (m_env_message_id);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("envelope");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ m_env_date = parser.get <IMAPParser::env_date>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_subject = parser.get <IMAPParser::env_subject>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_from = parser.get <IMAPParser::env_from>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_sender = parser.get <IMAPParser::env_sender>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_reply_to = parser.get <IMAPParser::env_reply_to>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_to = parser.get <IMAPParser::env_to>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_cc = parser.get <IMAPParser::env_cc>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_bcc = parser.get <IMAPParser::env_bcc>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_in_reply_to = parser.get <IMAPParser::env_in_reply_to>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_env_message_id = parser.get <IMAPParser::env_message_id>(line, &pos);
+
+ parser.check <one_char <')'> >(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::env_date* m_env_date;
+ IMAPParser::env_subject* m_env_subject;
+ IMAPParser::env_from* m_env_from;
+ IMAPParser::env_sender* m_env_sender;
+ IMAPParser::env_reply_to* m_env_reply_to;
+ IMAPParser::env_to* m_env_to;
+ IMAPParser::env_cc* m_env_cc;
+ IMAPParser::env_bcc* m_env_bcc;
+ IMAPParser::env_in_reply_to* m_env_in_reply_to;
+ IMAPParser::env_message_id* m_env_message_id;
+
+ public:
+
+ const IMAPParser::env_date* env_date() const { return (m_env_date); }
+ const IMAPParser::env_subject* env_subject() const { return (m_env_subject); }
+ const IMAPParser::env_from* env_from() const { return (m_env_from); }
+ const IMAPParser::env_sender* env_sender() const { return (m_env_sender); }
+ const IMAPParser::env_reply_to* env_reply_to() const { return (m_env_reply_to); }
+ const IMAPParser::env_to* env_to() const { return (m_env_to); }
+ const IMAPParser::env_cc* env_cc() const { return (m_env_cc); }
+ const IMAPParser::env_bcc* env_bcc() const { return (m_env_bcc); }
+ const IMAPParser::env_in_reply_to* env_in_reply_to() const { return (m_env_in_reply_to); }
+ const IMAPParser::env_message_id* env_message_id() const { return (m_env_message_id); }
+ };
+
+
+ //
+ // body_fld_desc ::= nstring
+ //
+
+ typedef nstring body_fld_desc;
+
+
+ //
+ // body_fld_id ::= nstring
+ //
+
+ typedef nstring body_fld_id;
+
+
+ //
+ // body_fld_md5 ::= nstring
+ //
+
+ typedef nstring body_fld_md5;
+
+
+ //
+ // body_fld_octets ::= number
+ //
+
+ typedef number body_fld_octets;
+
+
+ //
+ // body_fld_lines ::= number
+ //
+
+ typedef number body_fld_lines;
+
+
+ //
+ // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
+ // "QUOTED-PRINTABLE") <">) / string
+ //
+
+ typedef xstring body_fld_enc;
+
+
+ //
+ // body_fld_param_item ::= string SPACE string
+ //
+
+ class body_fld_param_item : public component
+ {
+ public:
+
+ body_fld_param_item()
+ : m_string1(NULL), m_string2(NULL)
+ {
+ }
+
+ ~body_fld_param_item()
+ {
+ delete (m_string1);
+ delete (m_string2);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_param_item");
+
+ size_t pos = *currentPos;
+
+ if (!parser.isStrict())
+ {
+ // Some servers send an <atom> instead of a <string> here:
+ // eg. ... (CHARSET "X-UNKNOWN") ...
+ if (!(m_string1 = parser.get <xstring>(line, &pos, true)))
+ {
+ std::auto_ptr <atom> at(parser.get <atom>(line, &pos));
+
+ m_string1 = new xstring();
+ m_string1->setValue(at->value());
+ }
+ }
+ else
+ {
+ m_string1 = parser.get <xstring>(line, &pos);
+ }
+
+ parser.check <SPACE>(line, &pos);
+ m_string2 = parser.get <xstring>(line, &pos);
+
+ DEBUG_FOUND("body_fld_param_item", "<" << m_string1->value() << ", " << m_string2->value() << ">");
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ xstring* m_string1;
+ xstring* m_string2;
+
+ public:
+
+ const xstring* string1() const { return (m_string1); }
+ const xstring* string2() const { return (m_string2); }
+ };
+
+
+ //
+ // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil
+ //
+
+ class body_fld_param : public component
+ {
+ public:
+
+ ~body_fld_param()
+ {
+ for (std::vector <body_fld_param_item*>::iterator it = m_items.begin() ;
+ it != m_items.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_param");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'('> >(line, &pos, true))
+ {
+ m_items.push_back(parser.get <body_fld_param_item>(line, &pos));
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ parser.check <SPACE>(line, &pos);
+ m_items.push_back(parser.get <body_fld_param_item>(line, &pos));
+ }
+ }
+ else
+ {
+ parser.check <NIL>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <body_fld_param_item*> m_items;
+
+ public:
+
+ const std::vector <body_fld_param_item*>& items() const { return (m_items); }
+ };
+
+
+ //
+ // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil
+ //
+
+ class body_fld_dsp : public component
+ {
+ public:
+
+ body_fld_dsp()
+ : m_string(NULL), m_body_fld_param(NULL)
+ {
+ }
+
+ ~body_fld_dsp()
+ {
+ delete (m_string);
+ delete (m_body_fld_param);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_dsp");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'('> >(line, &pos, true))
+ {
+ m_string = parser.get <xstring>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_param = parser.get <class body_fld_param>(line, &pos);
+ parser.check <one_char <')'> >(line, &pos);
+ }
+ else
+ {
+ parser.check <NIL>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ class xstring* m_string;
+ class body_fld_param* m_body_fld_param;
+
+ public:
+
+ const class xstring* str() const { return (m_string); }
+ const class body_fld_param* body_fld_param() const { return (m_body_fld_param); }
+ };
+
+
+ //
+ // body_fld_lang ::= nstring / "(" 1#string ")"
+ //
+
+ class body_fld_lang : public component
+ {
+ public:
+
+ ~body_fld_lang()
+ {
+ for (std::vector <xstring*>::iterator it = m_strings.begin() ;
+ it != m_strings.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_lang");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'('> >(line, &pos, true))
+ {
+ m_strings.push_back(parser.get <class xstring>(line, &pos));
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ parser.check <SPACE>(line, &pos);
+ m_strings.push_back(parser.get <class xstring>(line, &pos));
+ }
+ }
+ else
+ {
+ m_strings.push_back(parser.get <class nstring>(line, &pos));
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <xstring*> m_strings;
+
+ public:
+
+ const std::vector <xstring*>& strings() const { return (m_strings); }
+ };
+
+
+ //
+ // body_fields ::= body_fld_param SPACE body_fld_id SPACE
+ // body_fld_desc SPACE body_fld_enc SPACE
+ // body_fld_octets
+ //
+
+ class body_fields : public component
+ {
+ public:
+
+ body_fields()
+ : m_body_fld_param(NULL), m_body_fld_id(NULL),
+ m_body_fld_desc(NULL), m_body_fld_enc(NULL), m_body_fld_octets(NULL)
+ {
+ }
+
+ ~body_fields()
+ {
+ delete (m_body_fld_param);
+ delete (m_body_fld_id);
+ delete (m_body_fld_desc);
+ delete (m_body_fld_enc);
+ delete (m_body_fld_octets);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fields");
+
+ size_t pos = *currentPos;
+
+ m_body_fld_param = parser.get <IMAPParser::body_fld_param>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_id = parser.get <IMAPParser::body_fld_id>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_desc = parser.get <IMAPParser::body_fld_desc>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_enc = parser.get <IMAPParser::body_fld_enc>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_octets = parser.get <IMAPParser::body_fld_octets>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::body_fld_param* m_body_fld_param;
+ IMAPParser::body_fld_id* m_body_fld_id;
+ IMAPParser::body_fld_desc* m_body_fld_desc;
+ IMAPParser::body_fld_enc* m_body_fld_enc;
+ IMAPParser::body_fld_octets* m_body_fld_octets;
+
+ public:
+
+ const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); }
+ const IMAPParser::body_fld_id* body_fld_id() const { return (m_body_fld_id); }
+ const IMAPParser::body_fld_desc* body_fld_desc() const { return (m_body_fld_desc); }
+ const IMAPParser::body_fld_enc* body_fld_enc() const { return (m_body_fld_enc); }
+ const IMAPParser::body_fld_octets* body_fld_octets() const { return (m_body_fld_octets); }
+ };
+
+
+ //
+ // media_subtype ::= string
+ // ;; Defined in [MIME-IMT]
+ //
+
+ typedef xstring media_subtype;
+
+
+ //
+ // media_text ::= <"> "TEXT" <"> SPACE media_subtype
+ // ;; Defined in [MIME-IMT]
+ //
+
+ class media_text : public component
+ {
+ public:
+
+ media_text()
+ : m_media_subtype(NULL)
+ {
+ }
+
+ ~media_text()
+ {
+ delete (m_media_subtype);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_text");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.checkWithArg <special_atom>(line, &pos, "text");
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_subtype* m_media_subtype;
+
+ public:
+
+ const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); }
+ };
+
+
+ //
+ // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
+ // ;; Defined in [MIME-IMT]
+ //
+
+ class media_message : public component
+ {
+ public:
+
+ media_message()
+ : m_media_subtype(NULL)
+ {
+ }
+
+ ~media_message()
+ {
+ delete m_media_subtype;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_message");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.checkWithArg <special_atom>(line, &pos, "message");
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ //parser.check <one_char <'"'> >(line, &pos);
+ //parser.checkWithArg <special_atom>(line, &pos, "rfc822");
+ //parser.check <one_char <'"'> >(line, &pos);
+
+ m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_subtype* m_media_subtype;
+
+ public:
+
+ const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); }
+ };
+
+
+ //
+ // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
+ // "MESSAGE" / "VIDEO") <">) / string)
+ // SPACE media_subtype
+ // ;; Defined in [MIME-IMT]
+
+ class media_basic : public component
+ {
+ public:
+
+ media_basic()
+ : m_media_type(NULL), m_media_subtype(NULL)
+ {
+ }
+
+ ~media_basic()
+ {
+ delete (m_media_type);
+ delete (m_media_subtype);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_basic");
+
+ size_t pos = *currentPos;
+
+ m_media_type = parser.get <xstring>(line, &pos);
+
+ parser.check <SPACE>(line, &pos);
+
+ m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::xstring* m_media_type;
+ IMAPParser::media_subtype* m_media_subtype;
+
+ public:
+
+ const IMAPParser::xstring* media_type() const { return (m_media_type); }
+ const IMAPParser::media_subtype* media_subtype() const { return (m_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
+ //
+
+ class body_ext_1part : public component
+ {
+ public:
+
+ body_ext_1part()
+ : m_body_fld_md5(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL)
+ {
+ }
+
+ ~body_ext_1part()
+ {
+ delete (m_body_fld_md5);
+ delete (m_body_fld_dsp);
+ delete (m_body_fld_lang);
+
+ for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ;
+ it != m_body_extensions.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_ext_1part");
+
+ size_t pos = *currentPos;
+
+ m_body_fld_md5 = parser.get <IMAPParser::body_fld_md5>(line, &pos);
+
+ // [SPACE body_fld_dsp
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos);
+
+ // [SPACE body_fld_lang
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos);
+
+ // [SPACE 1#body_extension]
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_extensions.push_back
+ (parser.get <body_extension>(line, &pos));
+
+ parser.check <SPACE>(line, &pos, true);
+
+ body_extension* ext = NULL;
+
+ while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL)
+ {
+ m_body_extensions.push_back(ext);
+ parser.check <SPACE>(line, &pos, true);
+ }
+ }
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::body_fld_md5* m_body_fld_md5;
+ IMAPParser::body_fld_dsp* m_body_fld_dsp;
+ IMAPParser::body_fld_lang* m_body_fld_lang;
+
+ std::vector <body_extension*> m_body_extensions;
+
+ public:
+
+ const IMAPParser::body_fld_md5* body_fld_md5() const { return (m_body_fld_md5); }
+ const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); }
+ const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); }
+
+ const std::vector <body_extension*> body_extensions() const { return (m_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
+
+ class body_ext_mpart : public component
+ {
+ public:
+
+ body_ext_mpart()
+ : m_body_fld_param(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL)
+ {
+ }
+
+ ~body_ext_mpart()
+ {
+ delete (m_body_fld_param);
+ delete (m_body_fld_dsp);
+ delete (m_body_fld_lang);
+
+ for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ;
+ it != m_body_extensions.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_ext_mpart");
+
+ size_t pos = *currentPos;
+
+ m_body_fld_param = parser.get <IMAPParser::body_fld_param>(line, &pos);
+
+ // [SPACE body_fld_dsp SPACE body_fld_lang [SPACE 1#body_extension]]
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos);
+
+ // [SPACE 1#body_extension]
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_extensions.push_back
+ (parser.get <body_extension>(line, &pos));
+
+ parser.check <SPACE>(line, &pos, true);
+
+ body_extension* ext = NULL;
+
+ while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL)
+ {
+ m_body_extensions.push_back(ext);
+ parser.check <SPACE>(line, &pos, true);
+ }
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::body_fld_param* m_body_fld_param;
+ IMAPParser::body_fld_dsp* m_body_fld_dsp;
+ IMAPParser::body_fld_lang* m_body_fld_lang;
+
+ std::vector <body_extension*> m_body_extensions;
+
+ public:
+
+ const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); }
+ const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); }
+ const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); }
+
+ const std::vector <body_extension*> body_extensions() const { return (m_body_extensions); }
+ };
+
+
+ //
+ // body_type_basic ::= media_basic SPACE body_fields
+ // ;; MESSAGE subtype MUST NOT be "RFC822"
+ //
+
+ class body_type_basic : public component
+ {
+ public:
+
+ body_type_basic()
+ : m_media_basic(NULL), m_body_fields(NULL)
+ {
+ }
+
+ ~body_type_basic()
+ {
+ delete (m_media_basic);
+ delete (m_body_fields);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_basic");
+
+ size_t pos = *currentPos;
+
+ m_media_basic = parser.get <IMAPParser::media_basic>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_basic* m_media_basic;
+ IMAPParser::body_fields* m_body_fields;
+
+ public:
+
+ const IMAPParser::media_basic* media_basic() const { return (m_media_basic); }
+ const IMAPParser::body_fields* body_fields() const { return (m_body_fields); }
+ };
+
+
+ //
+ // body_type_msg ::= media_message SPACE body_fields SPACE envelope
+ // SPACE body SPACE body_fld_lines
+ //
+
+ class xbody;
+ typedef xbody body;
+
+ class body_type_msg : public component
+ {
+ public:
+
+ body_type_msg()
+ : m_media_message(NULL), m_body_fields(NULL),
+ m_envelope(NULL), m_body(NULL), m_body_fld_lines(NULL)
+ {
+ }
+
+ ~body_type_msg()
+ {
+ delete (m_media_message);
+ delete (m_body_fields);
+ delete (m_envelope);
+ delete (m_body);
+ delete (m_body_fld_lines);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_msg");
+
+ size_t pos = *currentPos;
+
+ m_media_message = parser.get <IMAPParser::media_message>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ // BUGFIX: made SPACE optional. This is not standard, but some servers
+ // seem to return responses like that...
+ m_envelope = parser.get <IMAPParser::envelope>(line, &pos);
+ parser.check <SPACE>(line, &pos, true);
+ m_body = parser.get <IMAPParser::xbody>(line, &pos);
+ parser.check <SPACE>(line, &pos, true);
+ m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_message* m_media_message;
+ IMAPParser::body_fields* m_body_fields;
+ IMAPParser::envelope* m_envelope;
+ IMAPParser::xbody* m_body;
+ IMAPParser::body_fld_lines* m_body_fld_lines;
+
+ public:
+
+ const IMAPParser::media_message* media_message() const { return (m_media_message); }
+ const IMAPParser::body_fields* body_fields() const { return (m_body_fields); }
+ const IMAPParser::envelope* envelope() const { return (m_envelope); }
+ const IMAPParser::xbody* body() const { return (m_body); }
+ const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); }
+ };
+
+
+ //
+ // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
+ //
+
+ class body_type_text : public component
+ {
+ public:
+
+ body_type_text()
+ : m_media_text(NULL),
+ m_body_fields(NULL), m_body_fld_lines(NULL)
+ {
+ }
+
+ ~body_type_text()
+ {
+ delete (m_media_text);
+ delete (m_body_fields);
+ delete (m_body_fld_lines);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_text");
+
+ size_t pos = *currentPos;
+
+ m_media_text = parser.get <IMAPParser::media_text>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_text* m_media_text;
+ IMAPParser::body_fields* m_body_fields;
+ IMAPParser::body_fld_lines* m_body_fld_lines;
+
+ public:
+
+ const IMAPParser::media_text* media_text() const { return (m_media_text); }
+ const IMAPParser::body_fields* body_fields() const { return (m_body_fields); }
+ const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); }
+ };
+
+
+ //
+ // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
+ // [SPACE body_ext_1part]
+ //
+
+ class body_type_1part : public component
+ {
+ public:
+
+ body_type_1part()
+ : m_body_type_basic(NULL), m_body_type_msg(NULL),
+ m_body_type_text(NULL), m_body_ext_1part(NULL)
+ {
+ }
+
+ ~body_type_1part()
+ {
+ delete (m_body_type_basic);
+ delete (m_body_type_msg);
+ delete (m_body_type_text);
+
+ delete (m_body_ext_1part);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_1part");
+
+ size_t pos = *currentPos;
+
+ if (!(m_body_type_text = parser.get <IMAPParser::body_type_text>(line, &pos, true)))
+ if (!(m_body_type_msg = parser.get <IMAPParser::body_type_msg>(line, &pos, true)))
+ m_body_type_basic = parser.get <IMAPParser::body_type_basic>(line, &pos);
+
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_body_ext_1part = parser.get <IMAPParser::body_ext_1part>(line, &pos, true);
+
+ if (!m_body_ext_1part)
+ --pos;
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::body_type_basic* m_body_type_basic;
+ IMAPParser::body_type_msg* m_body_type_msg;
+ IMAPParser::body_type_text* m_body_type_text;
+
+ IMAPParser::body_ext_1part* m_body_ext_1part;
+
+ public:
+
+ const IMAPParser::body_type_basic* body_type_basic() const { return (m_body_type_basic); }
+ const IMAPParser::body_type_msg* body_type_msg() const { return (m_body_type_msg); }
+ const IMAPParser::body_type_text* body_type_text() const { return (m_body_type_text); }
+
+ const IMAPParser::body_ext_1part* body_ext_1part() const { return (m_body_ext_1part); }
+ };
+
+
+ //
+ // body_type_mpart ::= 1*body SPACE media_subtype
+ // [SPACE body_ext_mpart]
+ //
+
+ class body_type_mpart : public component
+ {
+ public:
+
+ body_type_mpart()
+ : m_media_subtype(NULL), m_body_ext_mpart(NULL)
+ {
+ }
+
+ ~body_type_mpart()
+ {
+ delete (m_media_subtype);
+ delete (m_body_ext_mpart);
+
+ for (std::vector <xbody*>::iterator it = m_list.begin() ;
+ it != m_list.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_mpart");
+
+ size_t pos = *currentPos;
+
+ m_list.push_back(parser.get <xbody>(line, &pos));
+
+ for (xbody* b ; (b = parser.get <xbody>(line, &pos, true)) ; )
+ m_list.push_back(b);
+
+ parser.check <SPACE>(line, &pos);
+
+ m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos);
+
+ if (parser.check <SPACE>(line, &pos, true))
+ m_body_ext_mpart = parser.get <IMAPParser::body_ext_mpart>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::media_subtype* m_media_subtype;
+ IMAPParser::body_ext_mpart* m_body_ext_mpart;
+
+ std::vector <xbody*> m_list;
+
+ public:
+
+ const std::vector <IMAPParser::xbody*>& list() const { return (m_list); }
+
+ const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); }
+ const IMAPParser::body_ext_mpart* body_ext_mpart() const { return (m_body_ext_mpart); }
+ };
+
+
+ //
+ // xbody ::= "(" body_type_1part / body_type_mpart ")"
+ //
+
+ class xbody : public component
+ {
+ public:
+
+ xbody()
+ : m_body_type_1part(NULL), m_body_type_mpart(NULL)
+ {
+ }
+
+ ~xbody()
+ {
+ delete (m_body_type_1part);
+ delete (m_body_type_mpart);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ if (!(m_body_type_mpart = parser.get <IMAPParser::body_type_mpart>(line, &pos, true)))
+ m_body_type_1part = parser.get <IMAPParser::body_type_1part>(line, &pos);
+
+ parser.check <one_char <')'> >(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::body_type_1part* m_body_type_1part;
+ IMAPParser::body_type_mpart* m_body_type_mpart;
+
+ public:
+
+ const IMAPParser::body_type_1part* body_type_1part() const { return (m_body_type_1part); }
+ const IMAPParser::body_type_mpart* body_type_mpart() const { return (m_body_type_mpart); }
+ };
+
+
+ //
+ // uniqueid ::= nz_number
+ // ;; Strictly ascending
+ //
+ // 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 ")"
+
+ class msg_att_item : public component
+ {
+ public:
+
+ msg_att_item()
+ : m_date_time(NULL), m_number(NULL), m_envelope(NULL),
+ m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL),
+ m_section(NULL), m_mod_sequence_value(NULL)
+
+ {
+ }
+
+ ~msg_att_item()
+ {
+ delete (m_date_time);
+ delete (m_number);
+ delete (m_envelope);
+ delete (m_uniqueid);
+ delete (m_nstring);
+ delete (m_body);
+ delete (m_flag_list);
+ delete (m_section);
+ delete m_mod_sequence_value;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("msg_att_item");
+
+ size_t pos = *currentPos;
+
+ // "ENVELOPE" SPACE envelope
+ if (parser.checkWithArg <special_atom>(line, &pos, "envelope", true))
+ {
+ m_type = ENVELOPE;
+
+ parser.check <SPACE>(line, &pos);
+ m_envelope = parser.get <IMAPParser::envelope>(line, &pos);
+ }
+ // "FLAGS" SPACE "(" #(flag / "\Recent") ")"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "flags", true))
+ {
+ m_type = FLAGS;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos);
+ }
+ // "INTERNALDATE" SPACE date_time
+ else if (parser.checkWithArg <special_atom>(line, &pos, "internaldate", true))
+ {
+ m_type = INTERNALDATE;
+
+ parser.check <SPACE>(line, &pos);
+ m_date_time = parser.get <IMAPParser::date_time>(line, &pos);
+ }
+ // "RFC822" ".HEADER" SPACE nstring
+ else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.header", true))
+ {
+ m_type = RFC822_HEADER;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_nstring = parser.get <IMAPParser::nstring>(line, &pos);
+ }
+ // "RFC822" ".TEXT" SPACE nstring
+ else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.text", true))
+ {
+ m_type = RFC822_TEXT;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_nstring = parser.getWithArgs <IMAPParser::nstring>
+ (line, &pos, this, RFC822_TEXT);
+ }
+ // "RFC822.SIZE" SPACE number
+ else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.size", true))
+ {
+ m_type = RFC822_SIZE;
+
+ parser.check <SPACE>(line, &pos);
+ m_number = parser.get <IMAPParser::number>(line, &pos);
+ }
+ // "RFC822" SPACE nstring
+ else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822", true))
+ {
+ m_type = RFC822;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_nstring = parser.get <IMAPParser::nstring>(line, &pos);
+ }
+ // "BODY" "STRUCTURE" SPACE body
+ else if (parser.checkWithArg <special_atom>(line, &pos, "bodystructure", true))
+ {
+ m_type = BODY_STRUCTURE;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_body = parser.get <IMAPParser::body>(line, &pos);
+ }
+ // "BODY" section ["<" number ">"] SPACE nstring
+ // "BODY" SPACE body
+ else if (parser.checkWithArg <special_atom>(line, &pos, "body", true))
+ {
+ m_section = parser.get <IMAPParser::section>(line, &pos, true);
+
+ // "BODY" section ["<" number ">"] SPACE nstring
+ if (m_section != NULL)
+ {
+ m_type = BODY_SECTION;
+
+ if (parser.check <one_char <'<'> >(line, &pos, true))
+ {
+ m_number = parser.get <IMAPParser::number>(line, &pos);
+ parser.check <one_char <'>'> >(line, &pos);
+ }
+
+ parser.check <SPACE>(line, &pos);
+
+ m_nstring = parser.getWithArgs <IMAPParser::nstring>
+ (line, &pos, this, BODY_SECTION);
+ }
+ // "BODY" SPACE body
+ else
+ {
+ m_type = BODY;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_body = parser.get <IMAPParser::body>(line, &pos);
+ }
+ }
+ // "MODSEQ" SP "(" mod_sequence_value ")"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "modseq", true))
+ {
+ m_type = MODSEQ;
+
+ parser.check <SPACE>(line, &pos);
+ parser.check <one_char <'('> >(line, &pos);
+
+ m_mod_sequence_value = parser.get <IMAPParser::mod_sequence_value>(line, &pos);
+
+ parser.check <one_char <')'> >(line, &pos);
+ }
+ // "UID" SPACE uniqueid
+ else
+ {
+ m_type = UID;
+
+ parser.checkWithArg <special_atom>(line, &pos, "uid");
+ parser.check <SPACE>(line, &pos);
+
+ m_uniqueid = parser.get <nz_number>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ ENVELOPE,
+ FLAGS,
+ INTERNALDATE,
+ RFC822,
+ RFC822_SIZE,
+ RFC822_HEADER,
+ RFC822_TEXT,
+ BODY,
+ BODY_SECTION,
+ BODY_STRUCTURE,
+ UID,
+ MODSEQ
+ };
+
+ private:
+
+ Type m_type;
+
+ IMAPParser::date_time* m_date_time;
+ IMAPParser::number* m_number;
+ IMAPParser::envelope* m_envelope;
+ IMAPParser::nz_number* m_uniqueid;
+ IMAPParser::nstring* m_nstring;
+ IMAPParser::xbody* m_body;
+ IMAPParser::flag_list* m_flag_list;
+ IMAPParser::section* m_section;
+ IMAPParser::mod_sequence_value* m_mod_sequence_value;
+
+ public:
+
+ Type type() const { return (m_type); }
+
+ const IMAPParser::date_time* date_time() const { return (m_date_time); }
+ const IMAPParser::number* number() const { return (m_number); }
+ const IMAPParser::envelope* envelope() const { return (m_envelope); }
+ const IMAPParser::nz_number* unique_id() const { return (m_uniqueid); }
+ const IMAPParser::nstring* nstring() const { return (m_nstring); }
+ const IMAPParser::xbody* body() const { return (m_body); }
+ const IMAPParser::flag_list* flag_list() const { return (m_flag_list); }
+ const IMAPParser::section* section() const { return (m_section); }
+ const IMAPParser::mod_sequence_value* mod_sequence_value() { return m_mod_sequence_value; }
+ };
+
+
+ //
+ // msg_att ::= "(" 1#(msg_att_item) ")"
+ //
+
+ class msg_att : public component
+ {
+ public:
+
+ ~msg_att()
+ {
+ for (std::vector <msg_att_item*>::iterator it = m_items.begin() ;
+ it != m_items.end() ; ++it)
+ {
+ delete (*it);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("msg_att");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ m_items.push_back(parser.get <msg_att_item>(line, &pos));
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ parser.check <SPACE>(line, &pos);
+ m_items.push_back(parser.get <msg_att_item>(line, &pos));
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ std::vector <msg_att_item*> m_items;
+
+ public:
+
+ const std::vector <msg_att_item*>& items() const { return (m_items); }
+ };
+
+
+ //
+ // message_data ::= nz_number SPACE ("EXPUNGE" /
+ // ("FETCH" SPACE msg_att))
+ //
+
+ class message_data : public component
+ {
+ public:
+
+ message_data()
+ : m_number(0), m_msg_att(NULL)
+ {
+ }
+
+ ~message_data()
+ {
+ delete (m_msg_att);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("message_data");
+
+ size_t pos = *currentPos;
+
+ nz_number* num = parser.get <nz_number>(line, &pos);
+ m_number = static_cast <unsigned int>(num->value());
+ delete (num);
+
+ parser.check <SPACE>(line, &pos);
+
+ if (parser.checkWithArg <special_atom>(line, &pos, "expunge", true))
+ {
+ m_type = EXPUNGE;
+ }
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "fetch");
+
+ parser.check <SPACE>(line, &pos);
+
+ m_type = FETCH;
+ m_msg_att = parser.get <IMAPParser::msg_att>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ EXPUNGE,
+ FETCH
+ };
+
+ private:
+
+ Type m_type;
+ unsigned int m_number;
+ IMAPParser::msg_att* m_msg_att;
+
+ public:
+
+ Type type() const { return (m_type); }
+ unsigned int number() const { return (m_number); }
+ const IMAPParser::msg_att* msg_att() const { return (m_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 /
+ // 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
+
+ class resp_text_code : public component
+ {
+ public:
+
+ resp_text_code()
+ : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL),
+ m_text(NULL), m_capability_data(NULL)
+ {
+ }
+
+ ~resp_text_code()
+ {
+ delete (m_nz_number);
+ delete (m_atom);
+ delete (m_flag_list);
+ delete (m_text);
+ delete m_capability_data;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_text_code");
+
+ size_t pos = *currentPos;
+
+ // "ALERT"
+ if (parser.checkWithArg <special_atom>(line, &pos, "alert", true))
+ {
+ m_type = ALERT;
+ }
+ // "PARSE"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "parse", true))
+ {
+ m_type = PARSE;
+ }
+ // capability_data
+ else if ((m_capability_data = parser.get <IMAPParser::capability_data>(line, &pos, true)))
+ {
+ m_type = CAPABILITY;
+ }
+ // "PERMANENTFLAGS" SPACE flag_list
+ else if (parser.checkWithArg <special_atom>(line, &pos, "permanentflags", true))
+ {
+ m_type = PERMANENTFLAGS;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos);
+ }
+ // "READ-ONLY"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "read-only", true))
+ {
+ m_type = READ_ONLY;
+ }
+ // "READ-WRITE"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "read-write", true))
+ {
+ m_type = READ_WRITE;
+ }
+ // "TRYCREATE"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "trycreate", true))
+ {
+ m_type = TRYCREATE;
+ }
+ // "UIDVALIDITY" SPACE nz_number
+ else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true))
+ {
+ m_type = UIDVALIDITY;
+
+ parser.check <SPACE>(line, &pos);
+ m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos);
+ }
+ // "UIDNEXT" SPACE nz_number
+ else if (parser.checkWithArg <special_atom>(line, &pos, "uidnext", true))
+ {
+ m_type = UIDNEXT;
+
+ parser.check <SPACE>(line, &pos);
+ m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos);
+ }
+ // "UNSEEN" SPACE nz_number
+ else if (parser.checkWithArg <special_atom>(line, &pos, "unseen", true))
+ {
+ m_type = UNSEEN;
+
+ parser.check <SPACE>(line, &pos);
+ m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos);
+ }
+ // "HIGHESTMODSEQ" SP mod-sequence-value
+ else if (parser.checkWithArg <special_atom>(line, &pos, "highestmodseq", true))
+ {
+ m_type = HIGHESTMODSEQ;
+
+ parser.check <SPACE>(line, &pos);
+ m_mod_sequence_value = parser.get <IMAPParser::mod_sequence_value>(line, &pos);
+ }
+ // "NOMODSEQ"
+ else if (parser.checkWithArg <special_atom>(line, &pos, "nomodseq", true))
+ {
+ m_type = NOMODSEQ;
+ }
+ // "MODIFIED" SP sequence-set
+ else if (parser.checkWithArg <special_atom>(line, &pos, "modified", true))
+ {
+ m_type = MODIFIED;
+
+ parser.check <SPACE>(line, &pos);
+
+ m_sequence_set = parser.get <IMAPParser::sequence_set>(line, &pos);
+ }
+ // atom [SPACE 1*<any TEXT_CHAR except "]">]
+ else
+ {
+ m_type = OTHER;
+
+ m_atom = parser.get <IMAPParser::atom>(line, &pos);
+
+ if (parser.check <SPACE>(line, &pos, true))
+ m_text = parser.get <text_except <']'> >(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ // Extensions
+ HIGHESTMODSEQ,
+ NOMODSEQ,
+ MODIFIED,
+
+ // Standard IMAP
+ ALERT,
+ PARSE,
+ CAPABILITY,
+ PERMANENTFLAGS,
+ READ_ONLY,
+ READ_WRITE,
+ TRYCREATE,
+ UIDVALIDITY,
+ UIDNEXT,
+ UNSEEN,
+ OTHER
+ };
+
+ private:
+
+ Type m_type;
+
+ IMAPParser::nz_number* m_nz_number;
+ IMAPParser::atom* m_atom;
+ IMAPParser::flag_list* m_flag_list;
+ IMAPParser::text* m_text;
+ IMAPParser::mod_sequence_value* m_mod_sequence_value;
+ IMAPParser::sequence_set* m_sequence_set;
+ IMAPParser::capability_data* m_capability_data;
+
+ public:
+
+ Type type() const { return (m_type); }
+
+ const IMAPParser::nz_number* nz_number() const { return (m_nz_number); }
+ const IMAPParser::atom* atom() const { return (m_atom); }
+ const IMAPParser::flag_list* flag_list() const { return (m_flag_list); }
+ const IMAPParser::text* text() const { return (m_text); }
+ const IMAPParser::mod_sequence_value* mod_sequence_value() const { return m_mod_sequence_value; }
+ const IMAPParser::sequence_set* sequence_set() const { return m_sequence_set; }
+ const IMAPParser::capability_data* capability_data() const { return m_capability_data; }
+ };
+
+
+ //
+ // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
+ // ;; text SHOULD NOT begin with "[" or "="
+
+ class resp_text : public component
+ {
+ public:
+
+ resp_text()
+ : m_resp_text_code(NULL)
+ {
+ }
+
+ ~resp_text()
+ {
+ delete (m_resp_text_code);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_text");
+
+ size_t pos = *currentPos;
+
+ if (parser.check <one_char <'['> >(line, &pos, true))
+ {
+ m_resp_text_code = parser.get <IMAPParser::resp_text_code>(line, &pos);
+
+ parser.check <one_char <']'> >(line, &pos);
+ parser.check <SPACE>(line, &pos, true);
+ }
+
+ text_mime2* text1 = parser.get <text_mime2>(line, &pos, true);
+
+ if (text1 != NULL)
+ {
+ m_text = text1->value();
+ delete (text1);
+ }
+ else
+ {
+ IMAPParser::text* text2 =
+ parser.get <IMAPParser::text>(line, &pos, true);
+
+ if (text2 != NULL)
+ {
+ m_text = text2->value();
+ delete (text2);
+ }
+ else
+ {
+ // Empty response text
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_text_code* m_resp_text_code;
+ string m_text;
+
+ public:
+
+ const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); }
+ const string& text() const { return (m_text); }
+ };
+
+
+ //
+ // continue_req ::= "+" SPACE (resp_text / base64)
+ //
+
+ class continue_req : public component
+ {
+ public:
+
+ continue_req()
+ : m_resp_text(NULL)
+ {
+ }
+
+ ~continue_req()
+ {
+ delete (m_resp_text);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("continue_req");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'+'> >(line, &pos);
+
+ if (!parser.isStrict())
+ {
+ // Some servers do not send SPACE when response text is empty
+ if (parser.check <SPACE>(line, &pos, true))
+ m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos);
+ else
+ m_resp_text = new IMAPParser::resp_text(); // empty
+ }
+ else
+ {
+ parser.check <SPACE>(line, &pos);
+
+ m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos);
+ }
+
+ parser.check <CRLF>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_text* m_resp_text;
+
+ public:
+
+ const IMAPParser::resp_text* resp_text() const { return (m_resp_text); }
+ };
+
+
+ //
+ // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
+ // ;; Status condition
+ //
+
+ class resp_cond_state : public component
+ {
+ public:
+
+ resp_cond_state()
+ : m_resp_text(NULL), m_status(BAD)
+ {
+ }
+
+ ~resp_cond_state()
+ {
+ delete (m_resp_text);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_state");
+
+ size_t pos = *currentPos;
+
+ if (parser.checkWithArg <special_atom>(line, &pos, "ok", true))
+ {
+ m_status = OK;
+ }
+ else if (parser.checkWithArg <special_atom>(line, &pos, "no", true))
+ {
+ m_status = NO;
+ }
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "bad");
+ m_status = BAD;
+ }
+
+ parser.check <SPACE>(line, &pos);
+
+ m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+
+ enum Status
+ {
+ OK,
+ NO,
+ BAD
+ };
+
+ private:
+
+ IMAPParser::resp_text* m_resp_text;
+ Status m_status;
+
+ public:
+
+ const IMAPParser::resp_text* resp_text() const { return (m_resp_text); }
+ Status status() const { return (m_status); }
+ };
+
+
+ //
+ // resp_cond_bye ::= "BYE" SPACE resp_text
+ //
+
+ class resp_cond_bye : public component
+ {
+ public:
+
+ resp_cond_bye()
+ : m_resp_text(NULL)
+ {
+ }
+
+ ~resp_cond_bye()
+ {
+ delete (m_resp_text);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_bye");
+
+ size_t pos = *currentPos;
+
+ parser.checkWithArg <special_atom>(line, &pos, "bye");
+
+ parser.check <SPACE>(line, &pos);
+
+ m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_text* m_resp_text;
+
+ public:
+
+ const IMAPParser::resp_text* resp_text() const { return (m_resp_text); }
+ };
+
+
+ //
+ // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text
+ // ;; Authentication condition
+ //
+
+ class resp_cond_auth : public component
+ {
+ public:
+
+ resp_cond_auth()
+ : m_resp_text(NULL)
+ {
+ }
+
+ ~resp_cond_auth()
+ {
+ delete (m_resp_text);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_auth");
+
+ size_t pos = *currentPos;
+
+ if (parser.checkWithArg <special_atom>(line, &pos, "ok", true))
+ {
+ m_cond = OK;
+ }
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "preauth");
+
+ m_cond = PREAUTH;
+ }
+
+ parser.check <SPACE>(line, &pos);
+
+ m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+
+ enum Condition
+ {
+ OK,
+ PREAUTH
+ };
+
+ private:
+
+ Condition m_cond;
+ IMAPParser::resp_text* m_resp_text;
+
+ public:
+
+ Condition condition() const { return (m_cond); }
+ const IMAPParser::resp_text* resp_text() const { return (m_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"
+ //
+
+ class mailbox_data : public component
+ {
+ public:
+
+ mailbox_data()
+ : m_number(NULL), m_mailbox_flag_list(NULL), m_mailbox_list(NULL),
+ m_mailbox(NULL), m_text(NULL), m_status_att_list(NULL)
+ {
+ }
+
+ ~mailbox_data()
+ {
+ delete (m_number);
+ delete (m_mailbox_flag_list);
+ delete (m_mailbox_list);
+ delete (m_mailbox);
+ delete (m_text);
+
+ for (std::vector <nz_number*>::iterator it = m_search_nz_number_list.begin() ;
+ it != m_search_nz_number_list.end() ; ++it)
+ {
+ delete (*it);
+ }
+
+ delete m_status_att_list;
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_data");
+
+ size_t pos = *currentPos;
+
+ m_number = parser.get <IMAPParser::number>(line, &pos, true);
+
+ if (m_number)
+ {
+ parser.check <SPACE>(line, &pos);
+
+ if (parser.checkWithArg <special_atom>(line, &pos, "exists", true))
+ {
+ m_type = EXISTS;
+ }
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "recent");
+
+ m_type = RECENT;
+ }
+ }
+ else
+ {
+ // "FLAGS" SPACE mailbox_flag_list
+ if (parser.checkWithArg <special_atom>(line, &pos, "flags", true))
+ {
+ parser.check <SPACE>(line, &pos);
+
+ m_mailbox_flag_list = parser.get <IMAPParser::mailbox_flag_list>(line, &pos);
+
+ m_type = FLAGS;
+ }
+ // "LIST" SPACE mailbox_list
+ else if (parser.checkWithArg <special_atom>(line, &pos, "list", true))
+ {
+ parser.check <SPACE>(line, &pos);
+
+ m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos);
+
+ m_type = LIST;
+ }
+ // "LSUB" SPACE mailbox_list
+ else if (parser.checkWithArg <special_atom>(line, &pos, "lsub", true))
+ {
+ parser.check <SPACE>(line, &pos);
+
+ m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos);
+
+ m_type = LSUB;
+ }
+ // "MAILBOX" SPACE text
+ else if (parser.checkWithArg <special_atom>(line, &pos, "mailbox", true))
+ {
+ parser.check <SPACE>(line, &pos);
+
+ m_text = parser.get <IMAPParser::text>(line, &pos);
+
+ m_type = MAILBOX;
+ }
+ // "SEARCH" [SPACE 1#nz_number]
+ else if (parser.checkWithArg <special_atom>(line, &pos, "search", true))
+ {
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ m_search_nz_number_list.push_back
+ (parser.get <nz_number>(line, &pos));
+
+ while (parser.check <SPACE>(line, &pos, true))
+ {
+ m_search_nz_number_list.push_back
+ (parser.get <nz_number>(line, &pos));
+ }
+ }
+
+ m_type = SEARCH;
+ }
+ // "STATUS" SPACE mailbox SPACE
+ // "(" [status_att_list] ")"
+ else
+ {
+ parser.checkWithArg <special_atom>(line, &pos, "status");
+ parser.check <SPACE>(line, &pos);
+
+ m_mailbox = parser.get <IMAPParser::mailbox>(line, &pos);
+
+ parser.check <SPACE>(line, &pos);
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ m_status_att_list = parser.get <IMAPParser::status_att_list>(line, &pos, true);
+
+ parser.check <one_char <')'> >(line, &pos);
+
+ m_type = STATUS;
+ }
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ FLAGS,
+ LIST,
+ LSUB,
+ MAILBOX,
+ SEARCH,
+ STATUS,
+ EXISTS,
+ RECENT
+ };
+
+ private:
+
+ Type m_type;
+
+ IMAPParser::number* m_number;
+ IMAPParser::mailbox_flag_list* m_mailbox_flag_list;
+ IMAPParser::mailbox_list* m_mailbox_list;
+ IMAPParser::mailbox* m_mailbox;
+ IMAPParser::text* m_text;
+ std::vector <nz_number*> m_search_nz_number_list;
+ IMAPParser::status_att_list* m_status_att_list;
+
+ public:
+
+ Type type() const { return (m_type); }
+
+ const IMAPParser::number* number() const { return (m_number); }
+ const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); }
+ const IMAPParser::mailbox_list* mailbox_list() const { return (m_mailbox_list); }
+ const IMAPParser::mailbox* mailbox() const { return (m_mailbox); }
+ const IMAPParser::text* text() const { return (m_text); }
+ const std::vector <nz_number*>& search_nz_number_list() const { return (m_search_nz_number_list); }
+ const IMAPParser::status_att_list* status_att_list() const { return m_status_att_list; }
+ };
+
+
+ //
+ // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
+ // mailbox_data / message_data / capability_data) CRLF
+ //
+
+ class response_data : public component
+ {
+ public:
+
+ response_data()
+ : m_resp_cond_state(NULL), m_resp_cond_bye(NULL),
+ m_mailbox_data(NULL), m_message_data(NULL), m_capability_data(NULL)
+ {
+ }
+
+ ~response_data()
+ {
+ delete (m_resp_cond_state);
+ delete (m_resp_cond_bye);
+ delete (m_mailbox_data);
+ delete (m_message_data);
+ delete (m_capability_data);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_data");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'*'> >(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ if (!(m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos, true)))
+ if (!(m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos, true)))
+ if (!(m_mailbox_data = parser.get <IMAPParser::mailbox_data>(line, &pos, true)))
+ if (!(m_message_data = parser.get <IMAPParser::message_data>(line, &pos, true)))
+ m_capability_data = parser.get <IMAPParser::capability_data>(line, &pos);
+
+ if (!parser.isStrict())
+ {
+ // Allow SPACEs at end of line
+ while (parser.check <SPACE>(line, &pos, /* noThrow */ true))
+ ;
+ }
+
+ parser.check <CRLF>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_cond_state* m_resp_cond_state;
+ IMAPParser::resp_cond_bye* m_resp_cond_bye;
+ IMAPParser::mailbox_data* m_mailbox_data;
+ IMAPParser::message_data* m_message_data;
+ IMAPParser::capability_data* m_capability_data;
+
+ public:
+
+ const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); }
+ const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); }
+ const IMAPParser::mailbox_data* mailbox_data() const { return (m_mailbox_data); }
+ const IMAPParser::message_data* message_data() const { return (m_message_data); }
+ const IMAPParser::capability_data* capability_data() const { return (m_capability_data); }
+ };
+
+
+ class continue_req_or_response_data : public component
+ {
+ public:
+
+ continue_req_or_response_data()
+ : m_continue_req(NULL), m_response_data(NULL)
+ {
+ }
+
+ ~continue_req_or_response_data()
+ {
+ delete (m_continue_req);
+ delete (m_response_data);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("continue_req_or_response_data");
+
+ size_t pos = *currentPos;
+
+ if (!(m_continue_req = parser.get <IMAPParser::continue_req>(line, &pos, true)))
+ m_response_data = parser.get <IMAPParser::response_data>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::continue_req* m_continue_req;
+ IMAPParser::response_data* m_response_data;
+
+ public:
+
+ const IMAPParser::continue_req* continue_req() const { return (m_continue_req); }
+ const IMAPParser::response_data* response_data() const { return (m_response_data); }
+ };
+
+
+ //
+ // response_fatal ::= "*" SPACE resp_cond_bye CRLF
+ // ;; Server closes connection immediately
+ //
+
+ class response_fatal : public component
+ {
+ public:
+
+ response_fatal()
+ : m_resp_cond_bye(NULL)
+ {
+ }
+
+ ~response_fatal()
+ {
+ delete (m_resp_cond_bye);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_fatal");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'*'> >(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos);
+
+ if (!parser.isStrict())
+ {
+ // Allow SPACEs at end of line
+ while (parser.check <SPACE>(line, &pos, /* noThrow */ true))
+ ;
+ }
+
+ parser.check <CRLF>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_cond_bye* m_resp_cond_bye;
+
+ public:
+
+ const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); }
+ };
+
+
+ //
+ // response_tagged ::= tag SPACE resp_cond_state CRLF
+ //
+
+ class response_tagged : public component
+ {
+ public:
+
+ response_tagged()
+ : m_resp_cond_state(NULL)
+ {
+ }
+
+ ~response_tagged()
+ {
+ delete (m_resp_cond_state);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_tagged");
+
+ size_t pos = *currentPos;
+
+ parser.check <IMAPParser::xtag>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos);
+
+ if (!parser.isStrict())
+ {
+ // Allow SPACEs at end of line
+ while (parser.check <SPACE>(line, &pos, /* noThrow */ true))
+ ;
+ }
+
+ parser.check <CRLF>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::resp_cond_state* m_resp_cond_state;
+
+ public:
+
+ const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); }
+ };
+
+
+ //
+ // response_done ::= response_tagged / response_fatal
+ //
+
+ class response_done : public component
+ {
+ public:
+
+ response_done()
+ : m_response_tagged(NULL), m_response_fatal(NULL)
+ {
+ }
+
+ ~response_done()
+ {
+ delete (m_response_tagged);
+ delete (m_response_fatal);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_done");
+
+ size_t pos = *currentPos;
+
+ if (!(m_response_tagged = parser.get <IMAPParser::response_tagged>(line, &pos, true)))
+ m_response_fatal = parser.get <IMAPParser::response_fatal>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::response_tagged* m_response_tagged;
+ IMAPParser::response_fatal* m_response_fatal;
+
+ public:
+
+ const IMAPParser::response_tagged* response_tagged() const { return (m_response_tagged); }
+ const IMAPParser::response_fatal* response_fatal() const { return (m_response_fatal); }
+ };
+
+
+ //
+ // response ::= *(continue_req / response_data) response_done
+ //
+
+ class response : public component
+ {
+ public:
+
+ response()
+ : m_response_done(NULL)
+ {
+ }
+
+ ~response()
+ {
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::iterator
+ it = m_continue_req_or_response_data.begin() ;
+ it != m_continue_req_or_response_data.end() ; ++it)
+ {
+ delete (*it);
+ }
+
+ delete (m_response_done);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response");
+
+ 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, true)) != NULL)
+ {
+ m_continue_req_or_response_data.push_back(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)
+ m_response_done = parser.get <IMAPParser::response_done>(curLine, &pos);
+
+ *currentPos = pos;
+ }
+
+
+ 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;
+ }
+
+ private:
+
+ std::vector <IMAPParser::continue_req_or_response_data*> m_continue_req_or_response_data;
+ IMAPParser::response_done* m_response_done;
+
+ string m_errorLog;
+
+ public:
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& continue_req_or_response_data() const { return (m_continue_req_or_response_data); }
+ const IMAPParser::response_done* response_done() const { return (m_response_done); }
+ };
+
+
+ //
+ // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF
+ //
+
+ class greeting : public component
+ {
+ public:
+
+ greeting()
+ : m_resp_cond_auth(NULL), m_resp_cond_bye(NULL)
+ {
+ }
+
+ ~greeting()
+ {
+ delete (m_resp_cond_auth);
+ delete (m_resp_cond_bye);
+ }
+
+ void go(IMAPParser& parser, string& line, size_t* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("greeting");
+
+ size_t pos = *currentPos;
+
+ parser.check <one_char <'*'> >(line, &pos);
+ parser.check <SPACE>(line, &pos);
+
+ if (!(m_resp_cond_auth = parser.get <IMAPParser::resp_cond_auth>(line, &pos, true)))
+ m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos);
+
+ parser.check <CRLF>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ void setErrorLog(const string& errorLog)
+ {
+ m_errorLog = errorLog;
+ }
+
+ const string& getErrorLog() const
+ {
+ return m_errorLog;
+ }
+
+ private:
+
+ IMAPParser::resp_cond_auth* m_resp_cond_auth;
+ IMAPParser::resp_cond_bye* m_resp_cond_bye;
+
+ string m_errorLog;
+
+ public:
+
+ const IMAPParser::resp_cond_auth* resp_cond_auth() const { return (m_resp_cond_auth); }
+ const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); }
+ };
+
+
+
+ //
+ // The main functions used to parse a response
+ //
+
+ response* readResponse(literalHandler* lh = NULL)
+ {
+ size_t pos = 0;
+ string line = readLine();
+
+ m_literalHandler = lh;
+ response* resp = get <response>(line, &pos);
+ m_literalHandler = NULL;
+
+ resp->setErrorLog(lastLine());
+
+ return (resp);
+ }
+
+
+ greeting* readGreeting()
+ {
+ size_t pos = 0;
+ string line = readLine();
+
+ greeting* greet = get <greeting>(line, &pos);
+
+ greet->setErrorLog(lastLine());
+
+ return greet;
+ }
+
+
+ //
+ // Get a token and advance
+ //
+
+ template <class TYPE>
+ TYPE* get(string& line, size_t* currentPos,
+ const bool noThrow = false)
+ {
+ component* resp = new TYPE;
+ return internalGet <TYPE>(resp, line, currentPos, noThrow);
+ }
+
+
+ template <class TYPE, class ARG1_TYPE, class ARG2_TYPE>
+ TYPE* getWithArgs(string& line, size_t* currentPos,
+ ARG1_TYPE arg1, ARG2_TYPE arg2, const bool noThrow = false)
+ {
+ component* resp = new TYPE(arg1, arg2);
+ return internalGet <TYPE>(resp, line, currentPos, noThrow);
+ }
+
+
+private:
+
+ template <class TYPE>
+ TYPE* internalGet(component* resp, string& line, size_t* currentPos,
+ const bool noThrow = false)
+ {
+ const size_t oldPos = *currentPos;
+
+ try
+ {
+ resp->go(*this, line, currentPos);
+ }
+ catch (exceptions::operation_timed_out&)
+ {
+ // Always rethrow
+ throw;
+ }
+ catch (exception&)
+ {
+ *currentPos = oldPos;
+
+ delete (resp);
+ if (!noThrow) throw;
+ 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 a token and advance
+ //
+
+ template <class TYPE>
+ bool check(string& line, size_t* currentPos,
+ const bool noThrow = false)
+ {
+ const size_t oldPos = *currentPos;
+
+ try
+ {
+ TYPE term;
+ term.go(*this, line, currentPos);
+ }
+ catch (exceptions::operation_timed_out&)
+ {
+ // Always rethrow
+ throw;
+ }
+ catch (exception&)
+ {
+ *currentPos = oldPos;
+
+ if (!noThrow) throw;
+ return false;
+ }
+
+ return true;
+ }
+
+ template <class TYPE, class ARG_TYPE>
+ bool checkWithArg(string& line, size_t* currentPos,
+ const ARG_TYPE arg, const bool noThrow = false)
+ {
+ const size_t oldPos = *currentPos;
+
+ try
+ {
+ TYPE term(arg);
+ term.go(*this, line, currentPos);
+ }
+ catch (exceptions::operation_timed_out&)
+ {
+ // Always rethrow
+ throw;
+ }
+ catch (exception&)
+ {
+ *currentPos = oldPos;
+
+ if (!noThrow) throw;
+ return false;
+ }
+
+ return true;
+ }
+
+
+private:
+
+ weak_ptr <IMAPTag> m_tag;
+ weak_ptr <socket> m_socket;
+
+ utility::progressListener* m_progress;
+
+ bool m_strict;
+
+ literalHandler* m_literalHandler;
+
+ weak_ptr <timeoutHandler> m_timeoutHandler;
+
+
+ string m_buffer;
+
+ string m_lastLine;
+
+public:
+
+ //
+ // Read one 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
+
+ return (line);
+ }
+
+
+ //
+ // Read available data from socket stream
+ //
+
+ 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
+ {
+ platform::getHandler()->wait();
+ 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
+ {
+ platform::getHandler()->wait();
+ 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_progress)
+ m_progress->stop(count);
+ }
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+
+#endif // VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED
diff --git a/src/vmime/net/imap/IMAPSStore.cpp b/src/vmime/net/imap/IMAPSStore.cpp
new file mode 100644
index 00000000..c9e64f5b
--- /dev/null
+++ b/src/vmime/net/imap/IMAPSStore.cpp
@@ -0,0 +1,79 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/imap/IMAPSStore.hpp b/src/vmime/net/imap/IMAPSStore.hpp
new file mode 100644
index 00000000..9d27bdd0
--- /dev/null
+++ b/src/vmime/net/imap/IMAPSStore.hpp
@@ -0,0 +1,71 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/imap/IMAPServiceInfos.cpp b/src/vmime/net/imap/IMAPServiceInfos.cpp
new file mode 100644
index 00000000..46dbc2e1
--- /dev/null
+++ b/src/vmime/net/imap/IMAPServiceInfos.cpp
@@ -0,0 +1,137 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/imap/IMAPServiceInfos.hpp b/src/vmime/net/imap/IMAPServiceInfos.hpp
new file mode 100644
index 00000000..376f4476
--- /dev/null
+++ b/src/vmime/net/imap/IMAPServiceInfos.hpp
@@ -0,0 +1,91 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/imap/IMAPStore.cpp b/src/vmime/net/imap/IMAPStore.cpp
new file mode 100644
index 00000000..a1a8c9ca
--- /dev/null
+++ b/src/vmime/net/imap/IMAPStore.cpp
@@ -0,0 +1,267 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/exception.hpp"
+#include "vmime/platform.hpp"
+
+#include <map>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPStore::IMAPStore(shared_ptr <session> sess, 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 (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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 <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 <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()));
+}
+
+
+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());
+
+ try
+ {
+ m_connection->connect();
+ }
+ catch (std::exception&)
+ {
+ m_connection = null;
+ throw;
+ }
+}
+
+
+bool IMAPStore::isConnected() const
+{
+ return (m_connection && m_connection->isConnected());
+}
+
+
+bool IMAPStore::isIMAPS() const
+{
+ return m_isIMAPS;
+}
+
+
+bool IMAPStore::isSecuredConnection() const
+{
+ if (m_connection == NULL)
+ return false;
+
+ return m_connection->isSecuredConnection();
+}
+
+
+shared_ptr <connectionInfos> IMAPStore::getConnectionInfos() const
+{
+ if (m_connection == NULL)
+ 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();
+
+ m_connection->send(true, "NOOP", true);
+
+ std::auto_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/src/vmime/net/imap/IMAPStore.hpp b/src/vmime/net/imap/IMAPStore.hpp
new file mode 100644
index 00000000..f854fadf
--- /dev/null
+++ b/src/vmime/net/imap/IMAPStore.hpp
@@ -0,0 +1,120 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/imap/IMAPTag.cpp b/src/vmime/net/imap/IMAPTag.cpp
new file mode 100644
index 00000000..14d12788
--- /dev/null
+++ b/src/vmime/net/imap/IMAPTag.cpp
@@ -0,0 +1,122 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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);
+}
+
+
+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/src/vmime/net/imap/IMAPTag.hpp b/src/vmime/net/imap/IMAPTag.hpp
new file mode 100644
index 00000000..430a3b10
--- /dev/null
+++ b/src/vmime/net/imap/IMAPTag.hpp
@@ -0,0 +1,79 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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);
+ IMAPTag(const IMAPTag& tag);
+
+public:
+
+ IMAPTag();
+
+ IMAPTag& operator++(); // ++IMAPTag
+ const IMAPTag operator++(int); // IMAPTag++
+
+ int maximumNumber() const;
+ int number() const;
+
+ operator string() 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/src/vmime/net/imap/IMAPUtils.cpp b/src/vmime/net/imap/IMAPUtils.cpp
new file mode 100644
index 00000000..ff81ce71
--- /dev/null
+++ b/src/vmime/net/imap/IMAPUtils.cpp
@@ -0,0 +1,758 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 <[email protected]>
+
+ // 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;
+ 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)));
+}
+
+
+int IMAPUtils::folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list)
+{
+ // Get folder type
+ int type = folder::TYPE_CONTAINS_MESSAGES | folder::TYPE_CONTAINS_FOLDERS;
+ const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags();
+
+ for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ;
+ it != flags.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT)
+ type &= ~folder::TYPE_CONTAINS_MESSAGES;
+ }
+
+ if (type & folder::TYPE_CONTAINS_MESSAGES)
+ type &= ~folder::TYPE_CONTAINS_FOLDERS;
+
+ return (type);
+}
+
+
+int IMAPUtils::folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list)
+{
+ // Get folder flags
+ int folderFlags = folder::FLAG_CHILDREN;
+ const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags();
+
+ for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ;
+ it != flags.end() ; ++it)
+ {
+ if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT)
+ folderFlags |= folder::FLAG_NO_OPEN;
+ else if ((*it)->type() == IMAPParser::mailbox_flag::NOINFERIORS)
+ folderFlags &= ~folder::FLAG_CHILDREN;
+ }
+
+ return (folderFlags);
+}
+
+
+int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list* list)
+{
+ const std::vector <IMAPParser::flag*>& flagList = list->flags();
+ int flags = 0;
+
+ for (std::vector <IMAPParser::flag*>::const_iterator
+ it = flagList.begin() ; it != flagList.end() ; ++it)
+ {
+ switch ((*it)->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);
+}
+
+
+const string IMAPUtils::messageFlagList(const int flags)
+{
+ std::vector <string> flagList;
+
+ 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");
+
+ if (!flagList.empty())
+ {
+ std::ostringstream res;
+ res.imbue(std::locale::classic());
+
+ res << "(";
+
+ if (flagList.size() >= 2)
+ {
+ std::copy(flagList.begin(), flagList.end() - 1,
+ std::ostream_iterator <string>(res, " "));
+ }
+
+ res << *(flagList.end() - 1) << ")";
+
+ return (res.str());
+ }
+
+ return "";
+}
+
+
+// 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
+const string IMAPUtils::buildFetchRequest
+ (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->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 + ")]");
+ }
+ }
+
+ // Build the request text
+ std::ostringstream command;
+ command.imbue(std::locale::classic());
+
+ if (msgs.isUIDSet())
+ command << "UID FETCH " << messageSetToSequenceSet(msgs) << " (";
+ else
+ command << "FETCH " << messageSetToSequenceSet(msgs) << " (";
+
+ for (std::vector <string>::const_iterator it = items.begin() ;
+ it != items.end() ; ++it)
+ {
+ if (it != items.begin()) command << " ";
+ command << *it;
+ }
+
+ command << ")";
+
+ return command.str();
+}
+
+
+// static
+void IMAPUtils::convertAddressList
+ (const IMAPParser::address_list& src, mailboxList& dest)
+{
+ for (std::vector <IMAPParser::address*>::const_iterator
+ it = src.addresses().begin() ; it != src.addresses().end() ; ++it)
+ {
+ const IMAPParser::address& addr = **it;
+
+ 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)
+ {
+ }
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ if (!m_first)
+ m_oss << ",";
+
+ if (range.getFirst() == range.getLast())
+ 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
+ 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 (int 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 <int>& list() const
+ {
+ return m_list;
+ }
+
+public:
+
+ std::vector <int> m_list;
+};
+
+
+
+// static
+const string IMAPUtils::messageSetToSequenceSet(const messageSet& msgs)
+{
+ IMAPUIDMessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.str();
+}
+
+
+// static
+const std::vector <int> IMAPUtils::messageSetToNumberList(const messageSet& msgs)
+{
+ IMAPMessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.list();
+}
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP
+
diff --git a/src/vmime/net/imap/IMAPUtils.hpp b/src/vmime/net/imap/IMAPUtils.hpp
new file mode 100644
index 00000000..988b6a2c
--- /dev/null
+++ b/src/vmime/net/imap/IMAPUtils.hpp
@@ -0,0 +1,129 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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);
+
+ static int folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list);
+ static int folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list);
+
+ static int messageFlagsFromFlags(const IMAPParser::flag_list* list);
+
+ static const 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 const string buildFetchRequest
+ (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);
+
+ /** Returns a list of message sequence numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
+
+private:
+
+ static const string buildFetchRequestImpl
+ (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/src/vmime/net/imap/imap.hpp b/src/vmime/net/imap/imap.hpp
new file mode 100644
index 00000000..5e10619a
--- /dev/null
+++ b/src/vmime/net/imap/imap.hpp
@@ -0,0 +1,35 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/maildir/format/courierMaildirFormat.cpp b/src/vmime/net/maildir/format/courierMaildirFormat.cpp
new file mode 100644
index 00000000..6d460d5e
--- /dev/null
+++ b/src/vmime/net/maildir/format/courierMaildirFormat.cpp
@@ -0,0 +1,542 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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/src/vmime/net/maildir/format/courierMaildirFormat.hpp b/src/vmime/net/maildir/format/courierMaildirFormat.hpp
new file mode 100644
index 00000000..b8443426
--- /dev/null
+++ b/src/vmime/net/maildir/format/courierMaildirFormat.hpp
@@ -0,0 +1,121 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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/src/vmime/net/maildir/format/kmailMaildirFormat.cpp b/src/vmime/net/maildir/format/kmailMaildirFormat.cpp
new file mode 100644
index 00000000..975752a5
--- /dev/null
+++ b/src/vmime/net/maildir/format/kmailMaildirFormat.cpp
@@ -0,0 +1,320 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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/src/vmime/net/maildir/format/kmailMaildirFormat.hpp b/src/vmime/net/maildir/format/kmailMaildirFormat.hpp
new file mode 100644
index 00000000..98ca212e
--- /dev/null
+++ b/src/vmime/net/maildir/format/kmailMaildirFormat.hpp
@@ -0,0 +1,109 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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/src/vmime/net/maildir/maildir.hpp b/src/vmime/net/maildir/maildir.hpp
new file mode 100644
index 00000000..42bbbea4
--- /dev/null
+++ b/src/vmime/net/maildir/maildir.hpp
@@ -0,0 +1,34 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/maildir/maildirFolder.cpp b/src/vmime/net/maildir/maildirFolder.cpp
new file mode 100644
index 00000000..660178ff
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFolder.cpp
@@ -0,0 +1,1254 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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, 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()
+{
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ if (store)
+ {
+ if (m_open)
+ close(false);
+
+ store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ close(false);
+ }
+}
+
+
+void maildirFolder::onStoreDisconnected()
+{
+ m_store.reset();
+}
+
+
+int maildirFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+int maildirFolder::getType()
+{
+ if (m_path.isEmpty())
+ return (TYPE_CONTAINS_FOLDERS);
+ else
+ return (TYPE_CONTAINS_FOLDERS | TYPE_CONTAINS_MESSAGES);
+}
+
+
+int maildirFolder::getFlags()
+{
+ int flags = 0;
+
+ if (m_store.lock()->getFormat()->folderHasSubfolders(m_path))
+ flags |= FLAG_CHILDREN; // Contains at least one sub-folder
+
+ return (flags);
+}
+
+
+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 int /* type */)
+{
+ 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 (unsigned int 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
+ int 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 = m_messageInfos.size();
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Should not happen...
+ }
+}
+
+
+shared_ptr <message> maildirFolder::getMessage(const int 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 <int> numbers = maildirUtils::messageSetToNumberList(msgs);
+
+ std::vector <shared_ptr <message> > messages;
+ shared_ptr <maildirFolder> thisFolder = dynamicCast <maildirFolder>(shared_from_this());
+
+ for (std::vector <int>::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();
+ }
+}
+
+
+int 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 make_shared <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 =
+ make_shared <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 <int> nums = maildirUtils::messageSetToNumberList(msgs);
+
+ // 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 <int>::const_iterator it =
+ nums.begin() ; it != nums.end() ; ++it)
+ {
+ const int 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 != message::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 != message::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 != message::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();
+ }
+}
+
+
+void maildirFolder::addMessage(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);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void 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 == message::FLAG_UNDEFINED) ? 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 == message::FLAG_UNDEFINED) || !(flags & message::FLAG_SEEN))
+ m_unreadMessageCount++;
+
+ // Notification
+ std::vector <int> 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);
+ }
+ }
+}
+
+
+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);
+}
+
+
+void 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 <int> nums = maildirUtils::messageSetToNumberList(msgs);
+
+ try
+ {
+ for (std::vector <int>::const_iterator it =
+ nums.begin() ; it != nums.end() ; ++it)
+ {
+ const int 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);
+}
+
+
+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.
+ int count, unseen;
+ (*it)->status(count, unseen);
+
+ return;
+ }
+ }
+}
+
+
+void maildirFolder::status(int& count, int& unseen)
+{
+ count = 0;
+ unseen = 0;
+
+ shared_ptr <folderStatus> status = getStatus();
+
+ count = status->getMessageCount();
+ unseen = status->getUnseenCount();
+}
+
+
+shared_ptr <folderStatus> maildirFolder::getStatus()
+{
+ shared_ptr <maildirStore> store = m_store.lock();
+
+ const int 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 <int> nums;
+ nums.reserve(m_messageCount - oldCount);
+
+ for (int 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 <int> nums;
+ int unreadCount = 0;
+
+ for (int 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 <int>::size_type i = nums.size() ; i != 0 ; --i)
+ m_messageInfos.erase(m_messageInfos.begin() + (i - 1));
+ }
+
+ m_messageCount -= 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 make_shared <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");
+
+ 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(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);
+}
+
+
+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 int 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 <int> 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/src/vmime/net/maildir/maildirFolder.hpp b/src/vmime/net/maildir/maildirFolder.hpp
new file mode 100644
index 00000000..5cff53fc
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFolder.hpp
@@ -0,0 +1,190 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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() { }
+
+public:
+
+ maildirFolder(const folder::path& path, shared_ptr <maildirStore> store);
+
+ ~maildirFolder();
+
+
+ int getMode() const;
+
+ int getType();
+
+ int getFlags();
+
+ 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 int type);
+
+ bool exists();
+
+ void destroy();
+
+ bool isOpen() const;
+
+ shared_ptr <message> getMessage(const int num);
+ std::vector <shared_ptr <message> > getMessages(const messageSet& msgs);
+
+ int 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);
+
+ void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
+
+ void status(int& count, int& 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(shared_ptr <message> msg, const fetchAttributes& options);
+
+ int getFetchCapabilities() const;
+
+ std::vector <int> 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 int number) const;
+
+ void onStoreDisconnected();
+
+ void onClose();
+
+ void deleteMessagesImpl(const std::vector <int>& nums);
+ void setMessageFlagsImpl(const std::vector <int>& nums, const int flags, const int mode);
+
+ void copyMessagesImpl(const folder::path& dest, const std::vector <int>& 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;
+
+ int m_unreadMessageCount;
+ int 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/src/vmime/net/maildir/maildirFolderStatus.cpp b/src/vmime/net/maildir/maildirFolderStatus.cpp
new file mode 100644
index 00000000..9ee84dba
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFolderStatus.cpp
@@ -0,0 +1,88 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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)
+{
+}
+
+
+unsigned int maildirFolderStatus::getMessageCount() const
+{
+ return m_count;
+}
+
+
+unsigned int maildirFolderStatus::getUnseenCount() const
+{
+ return m_unseen;
+}
+
+
+void maildirFolderStatus::setMessageCount(const unsigned int count)
+{
+ m_count = count;
+}
+
+
+void maildirFolderStatus::setUnseenCount(const unsigned int 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/src/vmime/net/maildir/maildirFolderStatus.hpp b/src/vmime/net/maildir/maildirFolderStatus.hpp
new file mode 100644
index 00000000..155fb20f
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFolderStatus.hpp
@@ -0,0 +1,76 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ unsigned int getMessageCount() const;
+ unsigned int getUnseenCount() const;
+
+ shared_ptr <folderStatus> clone() const;
+
+
+ void setMessageCount(const unsigned int count);
+ void setUnseenCount(const unsigned int unseen);
+
+private:
+
+ unsigned int m_count;
+ unsigned int 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/src/vmime/net/maildir/maildirFormat.cpp b/src/vmime/net/maildir/maildirFormat.cpp
new file mode 100644
index 00000000..f7a3c8fe
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFormat.cpp
@@ -0,0 +1,109 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirStore> store)
+ : m_store(store)
+{
+}
+
+
+shared_ptr <maildirStore> maildirFormat::context::getStore() const
+{
+ return constCast <maildirStore>(m_store.lock());
+}
+
+
+//
+// maildirFormat
+//
+
+maildirFormat::maildirFormat(shared_ptr <context> ctx)
+ : m_context(ctx)
+{
+}
+
+
+shared_ptr <maildirFormat::context> maildirFormat::getContext()
+{
+ return m_context;
+}
+
+
+shared_ptr <const maildirFormat::context> maildirFormat::getContext() const
+{
+ return m_context;
+}
+
+
+// static
+shared_ptr <maildirFormat> maildirFormat::detect(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/src/vmime/net/maildir/maildirFormat.hpp b/src/vmime/net/maildir/maildirFormat.hpp
new file mode 100644
index 00000000..c0daf288
--- /dev/null
+++ b/src/vmime/net/maildir/maildirFormat.hpp
@@ -0,0 +1,195 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirStore> store);
+
+ shared_ptr <maildirStore> getStore() const;
+
+ 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(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(shared_ptr <context> ctx);
+
+
+ /** Returns the current context.
+ *
+ * @return current context
+ */
+ shared_ptr <context> getContext();
+
+ /** Returns the current context (const version).
+ *
+ * @return current context
+ */
+ shared_ptr <const 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/src/vmime/net/maildir/maildirMessage.cpp b/src/vmime/net/maildir/maildirMessage.cpp
new file mode 100644
index 00000000..a14f067e
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessage.cpp
@@ -0,0 +1,369 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirFolder> folder, const int 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()
+{
+ shared_ptr <maildirFolder> folder = m_folder.lock();
+
+ if (folder)
+ folder->unregisterMessage(this);
+}
+
+
+void maildirMessage::onFolderClosed()
+{
+ m_folder.reset();
+}
+
+
+int 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 == NULL)
+ throw exceptions::unfetched_object();
+
+ return m_structure;
+}
+
+
+shared_ptr <messageStructure> maildirMessage::getStructure()
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return m_structure;
+}
+
+
+shared_ptr <const header> maildirMessage::getHeader() const
+{
+ if (m_header == NULL)
+ 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(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(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(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 != NULL)
+ 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/src/vmime/net/maildir/maildirMessage.hpp b/src/vmime/net/maildir/maildirMessage.hpp
new file mode 100644
index 00000000..7480d49c
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessage.hpp
@@ -0,0 +1,116 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirFolder> folder, const int num);
+
+ ~maildirMessage();
+
+
+ int 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(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(shared_ptr <messagePart> p);
+
+ shared_ptr <vmime::message> getParsedMessage();
+
+private:
+
+ void fetch(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;
+
+ int 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/src/vmime/net/maildir/maildirMessagePart.cpp b/src/vmime/net/maildir/maildirMessagePart.cpp
new file mode 100644
index 00000000..6ae085c9
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessagePart.cpp
@@ -0,0 +1,155 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirMessagePart> parent, const int 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();
+}
+
+
+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 != NULL)
+ return m_structure;
+ else
+ return maildirMessageStructure::emptyStructure();
+}
+
+
+shared_ptr <messageStructure> maildirMessagePart::getStructure()
+{
+ if (m_structure != NULL)
+ return m_structure;
+ else
+ return maildirMessageStructure::emptyStructure();
+}
+
+
+const mediaType& maildirMessagePart::getType() const
+{
+ return m_mediaType;
+}
+
+
+size_t maildirMessagePart::getSize() const
+{
+ return m_size;
+}
+
+
+int maildirMessagePart::getNumber() const
+{
+ return m_number;
+}
+
+
+shared_ptr <const header> maildirMessagePart::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+ else
+ return m_header;
+}
+
+
+header& maildirMessagePart::getOrCreateHeader()
+{
+ if (m_header != NULL)
+ 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/src/vmime/net/maildir/maildirMessagePart.hpp b/src/vmime/net/maildir/maildirMessagePart.hpp
new file mode 100644
index 00000000..3a4be0f3
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessagePart.hpp
@@ -0,0 +1,99 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirMessagePart> parent, const int 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;
+ size_t getSize() const;
+ int 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;
+
+ int m_number;
+ size_t m_size;
+ mediaType m_mediaType;
+
+ 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/src/vmime/net/maildir/maildirMessageStructure.cpp b/src/vmime/net/maildir/maildirMessageStructure.cpp
new file mode 100644
index 00000000..f3b7cf59
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessageStructure.cpp
@@ -0,0 +1,98 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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(shared_ptr <maildirMessagePart> parent, const std::vector <shared_ptr <const vmime::bodyPart> >& list)
+{
+ for (unsigned int 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/src/vmime/net/maildir/maildirMessageStructure.hpp b/src/vmime/net/maildir/maildirMessageStructure.hpp
new file mode 100644
index 00000000..a43fc15c
--- /dev/null
+++ b/src/vmime/net/maildir/maildirMessageStructure.hpp
@@ -0,0 +1,76 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <maildirMessagePart> parent, const bodyPart& part);
+ maildirMessageStructure(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/src/vmime/net/maildir/maildirServiceInfos.cpp b/src/vmime/net/maildir/maildirServiceInfos.cpp
new file mode 100644
index 00000000..974a0c21
--- /dev/null
+++ b/src/vmime/net/maildir/maildirServiceInfos.cpp
@@ -0,0 +1,77 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/maildir/maildirServiceInfos.hpp b/src/vmime/net/maildir/maildirServiceInfos.hpp
new file mode 100644
index 00000000..70ddc6dc
--- /dev/null
+++ b/src/vmime/net/maildir/maildirServiceInfos.hpp
@@ -0,0 +1,71 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/maildir/maildirStore.cpp b/src/vmime/net/maildir/maildirStore.cpp
new file mode 100644
index 00000000..87e733e2
--- /dev/null
+++ b/src/vmime/net/maildir/maildirStore.cpp
@@ -0,0 +1,272 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, shared_ptr <security::authenticator> auth)
+ : store(sess, getInfosInstance(), auth), m_connected(false)
+{
+}
+
+
+maildirStore::~maildirStore()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+const string maildirStore::getProtocolName() const
+{
+ return "maildir";
+}
+
+
+shared_ptr <folder> maildirStore::getRootFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return make_shared <maildirFolder>
+ (folder::path(),
+ dynamicCast <maildirStore>(shared_from_this()));
+}
+
+
+shared_ptr <folder> maildirStore::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return make_shared <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 make_shared <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();
+ int 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/src/vmime/net/maildir/maildirStore.hpp b/src/vmime/net/maildir/maildirStore.hpp
new file mode 100644
index 00000000..efadfdfe
--- /dev/null
+++ b/src/vmime/net/maildir/maildirStore.hpp
@@ -0,0 +1,120 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/maildir/maildirUtils.cpp b/src/vmime/net/maildir/maildirUtils.cpp
new file mode 100644
index 00000000..77aac715
--- /dev/null
+++ b/src/vmime/net/maildir/maildirUtils.cpp
@@ -0,0 +1,273 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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:
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
+ list.push_back(i);
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
+ {
+ // Not supported
+ }
+
+public:
+
+ std::vector <int> list;
+};
+
+
+// static
+const std::vector <int> maildirUtils::messageSetToNumberList(const messageSet& msgs)
+{
+ maildirMessageSetEnumerator en;
+ 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/src/vmime/net/maildir/maildirUtils.hpp b/src/vmime/net/maildir/maildirUtils.hpp
new file mode 100644
index 00000000..82deefbb
--- /dev/null
+++ b/src/vmime/net/maildir/maildirUtils.hpp
@@ -0,0 +1,151 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <utility::file> dir);
+
+ /** Returns a list of message numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
+};
+
+
+} // maildir
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR
+
+#endif // VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED
diff --git a/src/vmime/net/message.cpp b/src/vmime/net/message.cpp
new file mode 100644
index 00000000..6765e73c
--- /dev/null
+++ b/src/vmime/net/message.cpp
@@ -0,0 +1,150 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 {
+
+
+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/src/vmime/net/message.hpp b/src/vmime/net/message.hpp
new file mode 100644
index 00000000..5bb62c53
--- /dev/null
+++ b/src/vmime/net/message.hpp
@@ -0,0 +1,355 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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
+{
+protected:
+
+ messagePart() { }
+ messagePart(const messagePart&) : object() { }
+
+ 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 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 int getNumber() const = 0;
+
+ /** 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
+{
+protected:
+
+ messageStructure() { }
+ messageStructure(const messageStructure&) : object() { }
+
+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
+{
+protected:
+
+ message() { }
+ message(const message&) : object() { }
+
+public:
+
+ virtual ~message() { }
+
+ /** 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 int 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'. */
+
+ FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned
+ by the flags() function). */
+ };
+
+ /** 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
+ (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(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/src/vmime/net/messageSet.cpp b/src/vmime/net/messageSet.cpp
new file mode 100644
index 00000000..2939042e
--- /dev/null
+++ b/src/vmime/net/messageSet.cpp
@@ -0,0 +1,369 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/messageSet.hpp"
+
+#include <iterator>
+#include <algorithm>
+#include <typeinfo>
+
+
+namespace vmime {
+namespace net {
+
+
+// messageRange
+
+messageRange::messageRange()
+{
+}
+
+
+messageRange::~messageRange()
+{
+}
+
+
+// numberMessageRange
+
+numberMessageRange::numberMessageRange(const int number)
+ : m_first(number), m_last(number)
+{
+ if (number < 1)
+ throw std::invalid_argument("number");
+}
+
+
+numberMessageRange::numberMessageRange(const int first, const int last)
+ : m_first(first), m_last(last)
+{
+ if (first < 1)
+ throw std::invalid_argument("first");
+ else if (last != -1 && last < first)
+ throw std::invalid_argument("last");
+}
+
+
+numberMessageRange::numberMessageRange(const numberMessageRange& other)
+ : messageRange(), m_first(other.m_first), m_last(other.m_last)
+{
+}
+
+
+int numberMessageRange::getFirst() const
+{
+ return m_first;
+}
+
+
+int 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::byNumber(const int number)
+{
+ messageSet set;
+ set.m_ranges.push_back(new numberMessageRange(number));
+
+ return set;
+}
+
+
+// static
+messageSet messageSet::byNumber(const int first, const int last)
+{
+ messageSet set;
+ set.m_ranges.push_back(new numberMessageRange(first, last));
+
+ return set;
+}
+
+
+// static
+messageSet messageSet::byNumber(const std::vector <int>& numbers)
+{
+ // Sort a copy of the list
+ std::vector <int> 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
+ int previous = -1, rangeStart = -1;
+ messageSet set;
+
+ for (std::vector <int>::const_iterator it = sortedNumbers.begin() ;
+ it != sortedNumbers.end() ; ++it)
+ {
+ const int current = *it;
+
+ if (current == previous)
+ continue; // skip duplicates
+
+ if (previous == -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 = -1U, rangeStart = -1U;
+ 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 == -1U)
+ {
+ 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;
+}
+
+
+} // net
+} // vmime
diff --git a/src/vmime/net/messageSet.hpp b/src/vmime/net/messageSet.hpp
new file mode 100644
index 00000000..6c7d7f44
--- /dev/null
+++ b/src/vmime/net/messageSet.hpp
@@ -0,0 +1,335 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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 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 int 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 int first, const int 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
+ */
+ int 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
+ */
+ int getLast() const;
+
+ void enumerate(messageSetEnumerator& en) const;
+
+ messageRange* clone() const;
+
+private:
+
+ int 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 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 int 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 int first, const int 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 <int>& 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;
+
+private:
+
+ messageSet();
+
+ std::vector <messageRange*> m_ranges;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MESSAGESET_HPP_INCLUDED
diff --git a/src/vmime/net/pop3/POP3Command.cpp b/src/vmime/net/pop3/POP3Command.cpp
new file mode 100644
index 00000000..6fe301ce
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Command.cpp
@@ -0,0 +1,230 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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)
+ : m_text(text)
+{
+}
+
+
+// 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::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;
+
+ return createCommand(cmd.str());
+}
+
+
+// static
+shared_ptr <POP3Command> POP3Command::PASS(const string& password)
+{
+ std::ostringstream cmd;
+ cmd.imbue(std::locale::classic());
+ cmd << "PASS " << password;
+
+ return createCommand(cmd.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)
+{
+ return shared_ptr <POP3Command>(new POP3Command(text));
+}
+
+
+const string POP3Command::getText() const
+{
+ return m_text;
+}
+
+
+void POP3Command::send(shared_ptr <POP3Connection> conn)
+{
+ conn->getSocket()->send(m_text + "\r\n");
+}
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
diff --git a/src/vmime/net/pop3/POP3Command.hpp b/src/vmime/net/pop3/POP3Command.hpp
new file mode 100644
index 00000000..cc3c4fd5
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Command.hpp
@@ -0,0 +1,113 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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> 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
+ * @return a new POP3Command object
+ */
+ static shared_ptr <POP3Command> createCommand(const string& text);
+
+ /** Sends this command over the specified connection.
+ *
+ * @param conn connection onto which the command will be sent
+ */
+ virtual void send(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;
+
+protected:
+
+ POP3Command(const string& text);
+ POP3Command(const POP3Command&);
+
+private:
+
+ string m_text;
+};
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
+
+#endif // VMIME_NET_POP3_POP3COMMAND_HPP_INCLUDED
diff --git a/src/vmime/net/pop3/POP3Connection.cpp b/src/vmime/net/pop3/POP3Connection.cpp
new file mode 100644
index 00000000..5fa923f4
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Connection.cpp
@@ -0,0 +1,675 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <POP3Store> store, 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)
+{
+}
+
+
+POP3Connection::~POP3Connection()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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);
+
+#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 <[email protected]>
+
+ 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& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on APOP/normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try APOP/normal authentication
+ }
+ }
+ catch (exception& e)
+ {
+ internalDisconnect();
+ throw e;
+ }
+ }
+#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);
+ 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 =
+ make_shared <security::sasl::SASLContext>();
+
+ 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();
+
+ POP3Command::AUTH(mech->getName())->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
+ m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
+ }
+ 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");
+ }
+ 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_timeoutHandler);
+
+ 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 <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/src/vmime/net/pop3/POP3Connection.hpp b/src/vmime/net/pop3/POP3Connection.hpp
new file mode 100644
index 00000000..3622f745
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Connection.hpp
@@ -0,0 +1,125 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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:
+
+ POP3Connection(shared_ptr <POP3Store> store, 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();
+
+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;
+
+ 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/src/vmime/net/pop3/POP3Folder.cpp b/src/vmime/net/pop3/POP3Folder.cpp
new file mode 100644
index 00000000..096de8af
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Folder.cpp
@@ -0,0 +1,729 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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, 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()
+{
+ shared_ptr <POP3Store> store = m_store.lock();
+
+ if (store)
+ {
+ if (m_open)
+ close(false);
+
+ store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ onClose();
+ }
+}
+
+
+int POP3Folder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+int POP3Folder::getType()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ if (m_path.isEmpty())
+ return (TYPE_CONTAINS_FOLDERS);
+ else if (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX")
+ return (TYPE_CONTAINS_MESSAGES);
+ else
+ throw exceptions::folder_not_found();
+}
+
+
+int POP3Folder::getFlags()
+{
+ return (0);
+}
+
+
+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 >> 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 int /* type */)
+{
+ 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 int 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 <int> numbers = POP3Utils::messageSetToNumberList(msgs);
+
+ std::vector <shared_ptr <message> > messages;
+ shared_ptr <POP3Folder> thisFolder(dynamicCast <POP3Folder>(shared_from_this()));
+
+ for (std::vector <int>::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();
+ }
+}
+
+
+int 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 make_shared <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(make_shared <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");
+
+ 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 <int, 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 <int, string>::const_iterator x = result.find(m->m_num);
+
+ if (x != result.end())
+ {
+ size_t size = 0;
+
+ std::istringstream iss((*x).second);
+ 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 <int, 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 <int, 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(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 >> 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());
+ }
+ }
+ }
+}
+
+
+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 make_shared <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 <int> nums = POP3Utils::messageSetToNumberList(msgs);
+
+ 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 <int>::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 <int> 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*, int>::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();
+}
+
+
+void POP3Folder::addMessage
+ (shared_ptr <vmime::message> /* msg */, const int /* flags */,
+ vmime::datetime* /* date */, utility::progressListener* /* progress */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::addMessage
+ (utility::inputStream& /* is */, const size_t /* size */, const int /* flags */,
+ vmime::datetime* /* date */, utility::progressListener* /* progress */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::copyMessages(const folder::path& /* dest */, const messageSet& /* msgs */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::status(int& count, int& unseen)
+{
+ count = 0;
+ unseen = 0;
+
+ shared_ptr <folderStatus> status = getStatus();
+
+ count = status->getMessageCount();
+ unseen = status->getUnseenCount();
+}
+
+
+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());
+
+
+ unsigned int count = 0;
+
+ std::istringstream iss(response->getText());
+ iss >> count;
+
+ shared_ptr <POP3FolderStatus> status = make_shared <POP3FolderStatus>();
+
+ status->setMessageCount(count);
+ status->setUnseenCount(count);
+
+ // Update local message count
+ if (m_messageCount != count)
+ {
+ const int oldCount = m_messageCount;
+
+ m_messageCount = count;
+
+ if (count > oldCount)
+ {
+ std::vector <int> nums;
+ nums.reserve(count - oldCount);
+
+ for (int 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 <int> 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/src/vmime/net/pop3/POP3Folder.hpp b/src/vmime/net/pop3/POP3Folder.hpp
new file mode 100644
index 00000000..27ea6e5f
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Folder.hpp
@@ -0,0 +1,157 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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&);
+
+public:
+
+ POP3Folder(const folder::path& path, shared_ptr <POP3Store> store);
+
+ ~POP3Folder();
+
+ int getMode() const;
+
+ int getType();
+
+ int getFlags();
+
+ 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 int type);
+
+ bool exists();
+
+ void destroy();
+
+ bool isOpen() const;
+
+ shared_ptr <message> getMessage(const int num);
+ std::vector <shared_ptr <message> > getMessages(const messageSet& msgs);
+
+ int 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);
+
+ void addMessage(shared_ptr <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const size_t size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressListener* progress = NULL);
+
+ void copyMessages(const folder::path& dest, const messageSet& msgs);
+
+ void status(int& count, int& 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(shared_ptr <message> msg, const fetchAttributes& options);
+
+ int getFetchCapabilities() const;
+
+ std::vector <int> 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;
+
+ int m_messageCount;
+
+ typedef std::map <POP3Message*, int> 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/src/vmime/net/pop3/POP3FolderStatus.cpp b/src/vmime/net/pop3/POP3FolderStatus.cpp
new file mode 100644
index 00000000..944379ac
--- /dev/null
+++ b/src/vmime/net/pop3/POP3FolderStatus.cpp
@@ -0,0 +1,88 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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)
+{
+}
+
+
+unsigned int POP3FolderStatus::getMessageCount() const
+{
+ return m_count;
+}
+
+
+unsigned int POP3FolderStatus::getUnseenCount() const
+{
+ return m_unseen;
+}
+
+
+void POP3FolderStatus::setMessageCount(const unsigned int count)
+{
+ m_count = count;
+}
+
+
+void POP3FolderStatus::setUnseenCount(const unsigned int 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/src/vmime/net/pop3/POP3FolderStatus.hpp b/src/vmime/net/pop3/POP3FolderStatus.hpp
new file mode 100644
index 00000000..70ba48b6
--- /dev/null
+++ b/src/vmime/net/pop3/POP3FolderStatus.hpp
@@ -0,0 +1,76 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ unsigned int getMessageCount() const;
+ unsigned int getUnseenCount() const;
+
+ shared_ptr <folderStatus> clone() const;
+
+
+ void setMessageCount(const unsigned int count);
+ void setUnseenCount(const unsigned int unseen);
+
+private:
+
+ unsigned int m_count;
+ unsigned int 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/src/vmime/net/pop3/POP3Message.cpp b/src/vmime/net/pop3/POP3Message.cpp
new file mode 100644
index 00000000..08523611
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Message.cpp
@@ -0,0 +1,250 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <POP3Folder> folder, const int num)
+ : m_folder(folder), m_num(num), m_size(-1), m_deleted(false)
+{
+ folder->registerMessage(this);
+}
+
+
+POP3Message::~POP3Message()
+{
+ shared_ptr <POP3Folder> folder = m_folder.lock();
+
+ if (folder)
+ folder->unregisterMessage(this);
+}
+
+
+void POP3Message::onFolderClosed()
+{
+ m_folder.reset();
+}
+
+
+int 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 = FLAG_RECENT;
+
+ 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 == NULL)
+ 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 = constCast <POP3Folder>(folder)->m_store.lock();
+
+ POP3Command::RETR(m_num)->send(store->getConnection());
+
+ try
+ {
+ POP3Response::readLargeResponse
+ (store->getConnection(), os, progress, m_size);
+ }
+ catch (exceptions::command_error& e)
+ {
+ throw exceptions::command_error("RETR", e.response());
+ }
+}
+
+
+void POP3Message::extractPart
+ (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(shared_ptr <messagePart> /* p */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Message::fetch(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/src/vmime/net/pop3/POP3Message.hpp b/src/vmime/net/pop3/POP3Message.hpp
new file mode 100644
index 00000000..87e71ba7
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Message.hpp
@@ -0,0 +1,121 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <POP3Folder> folder, const int num);
+
+ ~POP3Message();
+
+
+ int 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
+ (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(shared_ptr <messagePart> p);
+
+ shared_ptr <vmime::message> getParsedMessage();
+
+private:
+
+ void fetch(shared_ptr <POP3Folder> folder, const fetchAttributes& options);
+
+ void onFolderClosed();
+
+ weak_ptr <POP3Folder> m_folder;
+ int 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/src/vmime/net/pop3/POP3Response.cpp b/src/vmime/net/pop3/POP3Response.cpp
new file mode 100644
index 00000000..1dc5ee76
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Response.cpp
@@ -0,0 +1,428 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh)
+ : m_socket(sok), m_timeoutHandler(toh)
+{
+}
+
+
+// static
+shared_ptr <POP3Response> POP3Response::readResponse(shared_ptr <POP3Connection> conn)
+{
+ shared_ptr <POP3Response> resp = shared_ptr <POP3Response>
+ (new POP3Response(conn->getSocket(), conn->getTimeoutHandler()));
+
+ string buffer;
+ resp->readResponseImpl(buffer, /* multiLine */ false);
+
+ resp->m_firstLine = buffer;
+ resp->m_code = getResponseCode(buffer);
+ stripResponseCode(buffer, resp->m_text);
+
+ return resp;
+}
+
+
+// static
+shared_ptr <POP3Response> POP3Response::readMultilineResponse(shared_ptr <POP3Connection> conn)
+{
+ shared_ptr <POP3Response> resp = shared_ptr <POP3Response>
+ (new POP3Response(conn->getSocket(), conn->getTimeoutHandler()));
+
+ 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;
+
+ while (std::getline(iss, line, '\n'))
+ resp->m_lines.push_back(utility::stringUtils::trim(line));
+
+ return resp;
+}
+
+
+// static
+shared_ptr <POP3Response> POP3Response::readLargeResponse
+ (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()));
+
+ string firstLine;
+ resp->readResponseImpl(firstLine, os, progress, predictedSize);
+
+ resp->m_firstLine = firstLine;
+ resp->m_code = getResponseCode(firstLine);
+ stripResponseCode(firstLine, resp->m_text);
+
+ 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
+ {
+ platform::getHandler()->wait();
+ 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);
+ }
+ }
+}
+
+
+void 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())
+ {
+#if 0 // not supported
+ // Check for possible cancellation
+ if (progress && progress->cancel())
+ throw exceptions::operation_cancelled();
+#endif
+
+ // 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
+ {
+ platform::getHandler()->wait();
+ 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);
+}
+
+
+// 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/src/vmime/net/pop3/POP3Response.hpp b/src/vmime/net/pop3/POP3Response.hpp
new file mode 100644
index 00000000..20477b5e
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Response.hpp
@@ -0,0 +1,182 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+
+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(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(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
+ (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(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh);
+
+ void readResponseImpl(string& buffer, const bool multiLine);
+ void 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;
+
+ 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/src/vmime/net/pop3/POP3SStore.cpp b/src/vmime/net/pop3/POP3SStore.cpp
new file mode 100644
index 00000000..f1c3da74
--- /dev/null
+++ b/src/vmime/net/pop3/POP3SStore.cpp
@@ -0,0 +1,79 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/pop3/POP3SStore.hpp b/src/vmime/net/pop3/POP3SStore.hpp
new file mode 100644
index 00000000..e60b4ef8
--- /dev/null
+++ b/src/vmime/net/pop3/POP3SStore.hpp
@@ -0,0 +1,71 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/pop3/POP3ServiceInfos.cpp b/src/vmime/net/pop3/POP3ServiceInfos.cpp
new file mode 100644
index 00000000..4760d4f2
--- /dev/null
+++ b/src/vmime/net/pop3/POP3ServiceInfos.cpp
@@ -0,0 +1,143 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/pop3/POP3ServiceInfos.hpp b/src/vmime/net/pop3/POP3ServiceInfos.hpp
new file mode 100644
index 00000000..710d8be3
--- /dev/null
+++ b/src/vmime/net/pop3/POP3ServiceInfos.hpp
@@ -0,0 +1,93 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/pop3/POP3Store.cpp b/src/vmime/net/pop3/POP3Store.cpp
new file mode 100644
index 00000000..e6e95b1b
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Store.cpp
@@ -0,0 +1,240 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, shared_ptr <security::authenticator> auth, const bool secured)
+ : store(sess, getInfosInstance(), auth), m_isPOP3S(secured)
+{
+}
+
+
+POP3Store::~POP3Store()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+const string POP3Store::getProtocolName() const
+{
+ return "pop3";
+}
+
+
+shared_ptr <folder> POP3Store::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return make_shared <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 make_shared <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 make_shared <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());
+
+ try
+ {
+ m_connection->connect();
+ }
+ catch (std::exception&)
+ {
+ m_connection = null;
+ throw;
+ }
+}
+
+
+bool POP3Store::isPOP3S() const
+{
+ return m_isPOP3S;
+}
+
+
+bool POP3Store::isConnected() const
+{
+ return m_connection && m_connection->isConnected();
+}
+
+
+bool POP3Store::isSecuredConnection() const
+{
+ if (m_connection == NULL)
+ return false;
+
+ return m_connection->isSecuredConnection();
+}
+
+
+shared_ptr <connectionInfos> POP3Store::getConnectionInfos() const
+{
+ if (m_connection == NULL)
+ 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/src/vmime/net/pop3/POP3Store.hpp b/src/vmime/net/pop3/POP3Store.hpp
new file mode 100644
index 00000000..b35659a0
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Store.hpp
@@ -0,0 +1,116 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/pop3/POP3Utils.cpp b/src/vmime/net/pop3/POP3Utils.cpp
new file mode 100644
index 00000000..7ba65fff
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Utils.cpp
@@ -0,0 +1,114 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <POP3Response> response, std::map <int, string>& result)
+{
+ std::map <int, 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())
+ {
+ int 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 <int, string>::value_type(number, string(it, line.end())));
+ }
+ }
+ }
+}
+
+
+
+class POP3MessageSetEnumerator : public messageSetEnumerator
+{
+public:
+
+ void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
+ {
+ for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
+ list.push_back(i);
+ }
+
+ void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
+ {
+ // Not supported
+ }
+
+public:
+
+ std::vector <int> list;
+};
+
+
+// static
+const std::vector <int> POP3Utils::messageSetToNumberList(const messageSet& msgs)
+{
+ POP3MessageSetEnumerator en;
+ msgs.enumerate(en);
+
+ return en.list;
+}
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
+
diff --git a/src/vmime/net/pop3/POP3Utils.hpp b/src/vmime/net/pop3/POP3Utils.hpp
new file mode 100644
index 00000000..c7459efe
--- /dev/null
+++ b/src/vmime/net/pop3/POP3Utils.hpp
@@ -0,0 +1,86 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ (shared_ptr <POP3Response> response, std::map <int, string>& result);
+
+ /** Returns a list of message numbers given a message set.
+ *
+ * @param msgs message set
+ * @return list of message numbers
+ */
+ static const std::vector <int> messageSetToNumberList(const messageSet& msgs);
+};
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3
+
+#endif // VMIME_NET_POP3_POP3UTILS_HPP_INCLUDED
+
diff --git a/src/vmime/net/pop3/pop3.hpp b/src/vmime/net/pop3/pop3.hpp
new file mode 100644
index 00000000..366b1e4a
--- /dev/null
+++ b/src/vmime/net/pop3/pop3.hpp
@@ -0,0 +1,35 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/securedConnectionInfos.hpp b/src/vmime/net/securedConnectionInfos.hpp
new file mode 100644
index 00000000..8ed8b138
--- /dev/null
+++ b/src/vmime/net/securedConnectionInfos.hpp
@@ -0,0 +1,55 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/sendmail/sendmail.hpp b/src/vmime/net/sendmail/sendmail.hpp
new file mode 100644
index 00000000..b3692526
--- /dev/null
+++ b/src/vmime/net/sendmail/sendmail.hpp
@@ -0,0 +1,31 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/sendmail/sendmailServiceInfos.cpp b/src/vmime/net/sendmail/sendmailServiceInfos.cpp
new file mode 100644
index 00000000..21cac00c
--- /dev/null
+++ b/src/vmime/net/sendmail/sendmailServiceInfos.cpp
@@ -0,0 +1,78 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/sendmail/sendmailServiceInfos.hpp b/src/vmime/net/sendmail/sendmailServiceInfos.hpp
new file mode 100644
index 00000000..de94e392
--- /dev/null
+++ b/src/vmime/net/sendmail/sendmailServiceInfos.hpp
@@ -0,0 +1,71 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/sendmail/sendmailTransport.cpp b/src/vmime/net/sendmail/sendmailTransport.cpp
new file mode 100644
index 00000000..8ef18e3b
--- /dev/null
+++ b/src/vmime/net/sendmail/sendmailTransport.cpp
@@ -0,0 +1,230 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, shared_ptr <security::authenticator> auth)
+ : transport(sess, getInfosInstance(), auth), m_connected(false)
+{
+}
+
+
+sendmailTransport::~sendmailTransport()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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)
+{
+ // 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/src/vmime/net/sendmail/sendmailTransport.hpp b/src/vmime/net/sendmail/sendmailTransport.hpp
new file mode 100644
index 00000000..d1c6aec0
--- /dev/null
+++ b/src/vmime/net/sendmail/sendmailTransport.hpp
@@ -0,0 +1,103 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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());
+
+ 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/src/vmime/net/service.cpp b/src/vmime/net/service.cpp
new file mode 100644
index 00000000..c52ba592
--- /dev/null
+++ b/src/vmime/net/service.cpp
@@ -0,0 +1,152 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+#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(shared_ptr <session> sess, const serviceInfos& /* infos */,
+ 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();
+}
+
+
+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(shared_ptr <security::authenticator> auth)
+{
+ m_auth = auth;
+}
+
+
+#if VMIME_HAVE_TLS_SUPPORT
+
+void service::setCertificateVerifier(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(shared_ptr <socketFactory> sf)
+{
+ m_socketFactory = sf;
+}
+
+
+shared_ptr <socketFactory> service::getSocketFactory()
+{
+ return m_socketFactory;
+}
+
+
+void service::setTimeoutHandlerFactory(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/src/vmime/net/service.hpp b/src/vmime/net/service.hpp
new file mode 100644
index 00000000..6969ac20
--- /dev/null
+++ b/src/vmime/net/service.hpp
@@ -0,0 +1,233 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+#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
+{
+protected:
+
+ service(shared_ptr <session> sess, const serviceInfos& infos, 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(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(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(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, no timeout handler is used. Not all
+ * services support timeout handling.
+ *
+ * @param thf timeoutHandler factory
+ */
+ void setTimeoutHandlerFactory(shared_ptr <timeoutHandlerFactory> thf);
+
+ /** Return the factory used to create timeoutHandler objects for
+ * this service.
+ *
+ * @return timeoutHandler factory
+ */
+ shared_ptr <timeoutHandlerFactory> getTimeoutHandlerFactory();
+
+ /** 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;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_SERVICE_HPP_INCLUDED
diff --git a/src/vmime/net/serviceFactory.cpp b/src/vmime/net/serviceFactory.cpp
new file mode 100644
index 00000000..bbc9944a
--- /dev/null
+++ b/src/vmime/net/serviceFactory.cpp
@@ -0,0 +1,145 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ (shared_ptr <session> sess, const string& protocol,
+ shared_ptr <security::authenticator> auth)
+{
+ return (getServiceByProtocol(protocol)->create(sess, auth));
+}
+
+
+shared_ptr <service> serviceFactory::create
+ (shared_ptr <session> sess, const utility::url& u,
+ 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(shared_ptr <registeredService> reg)
+{
+ m_services.push_back(reg);
+}
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
diff --git a/src/vmime/net/serviceFactory.hpp b/src/vmime/net/serviceFactory.hpp
new file mode 100644
index 00000000..9295b345
--- /dev/null
+++ b/src/vmime/net/serviceFactory.hpp
@@ -0,0 +1,165 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ (shared_ptr <session> sess,
+ 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(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
+ (shared_ptr <session> sess,
+ const string& protocol,
+ 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
+ (shared_ptr <session> sess,
+ const utility::url& u,
+ 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/src/vmime/net/serviceInfos.cpp b/src/vmime/net/serviceInfos.cpp
new file mode 100644
index 00000000..8de0529e
--- /dev/null
+++ b/src/vmime/net/serviceInfos.cpp
@@ -0,0 +1,167 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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);
+
+#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(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/src/vmime/net/serviceInfos.hpp b/src/vmime/net/serviceInfos.hpp
new file mode 100644
index 00000000..6e3209ca
--- /dev/null
+++ b/src/vmime/net/serviceInfos.hpp
@@ -0,0 +1,246 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 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;
+
+#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(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(shared_ptr <session> s, const property& p) const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_SERVICEINFOS_HPP_INCLUDED
diff --git a/src/vmime/net/serviceRegistration.inl b/src/vmime/net/serviceRegistration.inl
new file mode 100644
index 00000000..2366fe01
--- /dev/null
+++ b/src/vmime/net/serviceRegistration.inl
@@ -0,0 +1,98 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ (shared_ptr <session> sess,
+ 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/src/vmime/net/session.cpp b/src/vmime/net/session.cpp
new file mode 100644
index 00000000..36b9f2c3
--- /dev/null
+++ b/src/vmime/net/session.cpp
@@ -0,0 +1,158 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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()
+ : m_tlsProps(make_shared <tls::TLSProperties>())
+{
+}
+
+
+session::session(const session& sess)
+ : object(), m_props(sess.m_props),
+ m_tlsProps(make_shared <tls::TLSProperties>(*sess.m_tlsProps))
+{
+}
+
+
+session::session(const propertySet& props)
+ : m_props(props), m_tlsProps(make_shared <tls::TLSProperties>())
+{
+}
+
+
+session::~session()
+{
+}
+
+
+shared_ptr <transport> session::getTransport(shared_ptr <security::authenticator> auth)
+{
+ return (getTransport(m_props["transport.protocol"], auth));
+}
+
+
+shared_ptr <transport> session::getTransport
+ (const string& protocol, 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, 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(shared_ptr <security::authenticator> auth)
+{
+ return (getStore(m_props["store.protocol"], auth));
+}
+
+
+shared_ptr <store> session::getStore
+ (const string& protocol, 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, 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);
+}
+
+
+void session::setTLSProperties(shared_ptr <tls::TLSProperties> tlsProps)
+{
+ m_tlsProps = make_shared <tls::TLSProperties>(*tlsProps);
+}
+
+
+shared_ptr <tls::TLSProperties> session::getTLSProperties() const
+{
+ return m_tlsProps;
+}
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
diff --git a/src/vmime/net/session.hpp b/src/vmime/net/session.hpp
new file mode 100644
index 00000000..a7e0ea1a
--- /dev/null
+++ b/src/vmime/net/session.hpp
@@ -0,0 +1,178 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+#include "vmime/net/tls/TLSProperties.hpp"
+
+#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:
+
+ session();
+ session(const session& sess);
+ session(const propertySet& props);
+
+ virtual ~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
+ (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,
+ 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,
+ 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(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,
+ 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:[email protected]/")
+ * @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,
+ 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();
+
+ /** Set properties for SSL/TLS secured connections in this session.
+ *
+ * @param tlsProps SSL/TLS properties
+ */
+ void setTLSProperties(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;
+
+private:
+
+ propertySet m_props;
+
+ shared_ptr <tls::TLSProperties> m_tlsProps;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_SESSION_HPP_INCLUDED
diff --git a/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp b/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp
new file mode 100644
index 00000000..69f63bc9
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.cpp
@@ -0,0 +1,145 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <SMTPConnection> conn)
+ : m_connection(conn), m_bufferSize(0), m_chunkCount(0)
+{
+}
+
+
+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 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(data, data + 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;
+}
+
+
+size_t SMTPChunkingOutputStreamAdapter::getBlockSize()
+{
+ return sizeof(m_buffer);
+}
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
diff --git a/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp b/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp
new file mode 100644
index 00000000..cfb3f50f
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPChunkingOutputStreamAdapter.hpp
@@ -0,0 +1,85 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+
+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(shared_ptr <SMTPConnection> conn);
+
+ 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;
+};
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
+
+#endif // VMIME_NET_SMTP_SMTPCHUNKINGOUTPUTSTREAMADAPTER_HPP_INCLUDED
diff --git a/src/vmime/net/smtp/SMTPCommand.cpp b/src/vmime/net/smtp/SMTPCommand.cpp
new file mode 100644
index 00000000..949ab0c1
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPCommand.cpp
@@ -0,0 +1,214 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/mailbox.hpp"
+#include "vmime/utility/outputStreamAdapter.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPCommand::SMTPCommand(const string& text)
+ : m_text(text)
+{
+}
+
+
+// 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::STARTTLS()
+{
+ return createCommand("STARTTLS");
+}
+
+
+// static
+shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8)
+{
+ return MAIL(mbox, utf8, 0);
+}
+
+
+// static
+shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const size_t size)
+{
+ 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 (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)
+{
+ 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 << ">";
+
+ 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)
+{
+ return shared_ptr <SMTPCommand>(new SMTPCommand(text));
+}
+
+
+const string SMTPCommand::getText() const
+{
+ return m_text;
+}
+
+
+void SMTPCommand::writeToSocket(shared_ptr <socket> sok)
+{
+ sok->send(m_text + "\r\n");
+}
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
diff --git a/src/vmime/net/smtp/SMTPCommand.hpp b/src/vmime/net/smtp/SMTPCommand.hpp
new file mode 100644
index 00000000..dbb0888b
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPCommand.hpp
@@ -0,0 +1,111 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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;
+
+
+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> STARTTLS();
+ static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8);
+ static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size);
+ static shared_ptr <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8);
+ 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);
+
+ /** Sends this command to the specified socket.
+ *
+ * @param sok socket to which the command will be written
+ */
+ virtual void writeToSocket(shared_ptr <socket> sok);
+
+ /** Returns the full text of the command, including command name
+ * and parameters (if any).
+ *
+ * @return command text (eg. "RCPT TO:<[email protected]>")
+ */
+ virtual const string getText() const;
+
+protected:
+
+ SMTPCommand(const string& text);
+ SMTPCommand(const SMTPCommand&);
+
+private:
+
+ string m_text;
+};
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
+
+#endif // VMIME_NET_SMTP_SMTPCOMMAND_HPP_INCLUDED
diff --git a/src/vmime/net/smtp/SMTPCommandSet.cpp b/src/vmime/net/smtp/SMTPCommandSet.cpp
new file mode 100644
index 00000000..3e03427c
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPCommandSet.cpp
@@ -0,0 +1,144 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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(shared_ptr <socket> sok)
+{
+ 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);
+ }
+ }
+
+ 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);
+
+ 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();
+}
+
+
+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/src/vmime/net/smtp/SMTPCommandSet.hpp b/src/vmime/net/smtp/SMTPCommandSet.hpp
new file mode 100644
index 00000000..8e744c2b
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPCommandSet.hpp
@@ -0,0 +1,105 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(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(shared_ptr <socket> sok);
+
+ const string getText() 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/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp
new file mode 100644
index 00000000..26be25db
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPConnection.cpp
@@ -0,0 +1,618 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+#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(shared_ptr <SMTPTransport> transport, 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)
+{
+}
+
+
+SMTPConnection::~SMTPConnection()
+{
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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);
+
+#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);
+
+ 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
+ // First, try SASL authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
+ {
+ try
+ {
+ authenticateSASL();
+
+ m_authenticated = true;
+ return;
+ }
+ catch (exceptions::authentication_error& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try normal authentication
+ }
+ }
+ catch (exception& e)
+ {
+ internalDisconnect();
+ throw e;
+ }
+ }
+#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 =
+ make_shared <security::sasl::SASLContext>();
+
+ 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();
+
+ 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
+ m_socket->send(saslContext->encodeB64(resp, respLen) + "\r\n");
+ }
+ 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");
+ }
+ 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_timeoutHandler);
+
+ 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()
+{
+ try
+ {
+ sendRequest(SMTPCommand::QUIT());
+ readResponse();
+ }
+ 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(shared_ptr <SMTPCommand> cmd)
+{
+ cmd->writeToSocket(m_socket);
+}
+
+
+shared_ptr <SMTPResponse> SMTPConnection::readResponse()
+{
+ shared_ptr <SMTPResponse> resp = SMTPResponse::readResponse
+ (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 <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/src/vmime/net/smtp/SMTPConnection.hpp b/src/vmime/net/smtp/SMTPConnection.hpp
new file mode 100644
index 00000000..cc59ef34
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPConnection.hpp
@@ -0,0 +1,129 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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(shared_ptr <SMTPTransport> transport, shared_ptr <security::authenticator> auth);
+ virtual ~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();
+
+ void sendRequest(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;
+
+ 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/src/vmime/net/smtp/SMTPExceptions.cpp b/src/vmime/net/smtp/SMTPExceptions.cpp
new file mode 100644
index 00000000..0c3112c0
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPExceptions.cpp
@@ -0,0 +1,151 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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";
+}
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
diff --git a/src/vmime/net/smtp/SMTPExceptions.hpp b/src/vmime/net/smtp/SMTPExceptions.hpp
new file mode 100644
index 00000000..75842042
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPExceptions.hpp
@@ -0,0 +1,127 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_SMTP
+
+#endif // VMIME_NET_SMTP_SMTPEXCEPTIONS_HPP_INCLUDED
+
diff --git a/src/vmime/net/smtp/SMTPResponse.cpp b/src/vmime/net/smtp/SMTPResponse.cpp
new file mode 100644
index 00000000..f7980351
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPResponse.cpp
@@ -0,0 +1,334 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 <cctype>
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPResponse::SMTPResponse(shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st)
+ : m_socket(sok), m_timeoutHandler(toh),
+ 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
+ (shared_ptr <socket> sok, shared_ptr <timeoutHandler> toh, const state& st)
+{
+ shared_ptr <SMTPResponse> resp = shared_ptr <SMTPResponse>(new SMTPResponse(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;
+
+ 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
+ {
+ platform::getHandler()->wait();
+ 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);
+
+ 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/src/vmime/net/smtp/SMTPResponse.hpp b/src/vmime/net/smtp/SMTPResponse.hpp
new file mode 100644
index 00000000..000448ac
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPResponse.hpp
@@ -0,0 +1,186 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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;
+
+
+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 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(shared_ptr <socket> sok, 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(shared_ptr <socket> sok, 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;
+
+ 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/src/vmime/net/smtp/SMTPSTransport.cpp b/src/vmime/net/smtp/SMTPSTransport.cpp
new file mode 100644
index 00000000..ab64d49d
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPSTransport.cpp
@@ -0,0 +1,79 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/smtp/SMTPSTransport.hpp b/src/vmime/net/smtp/SMTPSTransport.hpp
new file mode 100644
index 00000000..7782f711
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPSTransport.hpp
@@ -0,0 +1,71 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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/src/vmime/net/smtp/SMTPServiceInfos.cpp b/src/vmime/net/smtp/SMTPServiceInfos.cpp
new file mode 100644
index 00000000..532bb8b8
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPServiceInfos.cpp
@@ -0,0 +1,146 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/smtp/SMTPServiceInfos.hpp b/src/vmime/net/smtp/SMTPServiceInfos.hpp
new file mode 100644
index 00000000..f783194d
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPServiceInfos.hpp
@@ -0,0 +1,95 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/smtp/SMTPTransport.cpp b/src/vmime/net/smtp/SMTPTransport.cpp
new file mode 100644
index 00000000..0020d010
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPTransport.cpp
@@ -0,0 +1,420 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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 (vmime::exception&)
+ {
+ // Ignore
+ }
+}
+
+
+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());
+
+ try
+ {
+ m_connection->connect();
+ }
+ catch (std::exception&)
+ {
+ m_connection = null;
+ throw;
+ }
+}
+
+
+bool SMTPTransport::isConnected() const
+{
+ return m_connection && m_connection->isConnected();
+}
+
+
+bool SMTPTransport::isSecuredConnection() const
+{
+ if (m_connection == NULL)
+ return false;
+
+ return m_connection->isSecuredConnection();
+}
+
+
+shared_ptr <connectionInfos> SMTPTransport::getConnectionInfos() const
+{
+ if (m_connection == NULL)
+ 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());
+ }
+}
+
+
+void SMTPTransport::sendEnvelope
+ (const mailbox& expeditor, const mailboxList& recipients,
+ const mailbox& sender, bool sendDATACommand,
+ const size_t size)
+{
+ // If no recipient/expeditor was found, throw an exception
+ if (recipients.isEmpty())
+ throw exceptions::no_recipient();
+ else if (expeditor.isEmpty())
+ throw exceptions::no_expeditor();
+
+
+ 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());
+
+ // Emit the "MAIL" command
+ const bool hasSMTPUTF8 = m_connection->hasExtension("SMTPUTF8");
+ const bool hasSize = m_connection->hasExtension("SIZE");
+
+ if (!sender.isEmpty())
+ commands->addCommand(SMTPCommand::MAIL(sender, hasSMTPUTF8, hasSize ? size : 0));
+ else
+ commands->addCommand(SMTPCommand::MAIL(expeditor, hasSMTPUTF8, hasSize ? size : 0));
+
+ // 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));
+ }
+
+ // Prepare sending of message data
+ if (sendDATACommand)
+ commands->addCommand(SMTPCommand::DATA());
+
+ // Read response for "RSET" command
+ if (needReset)
+ {
+ commands->writeToSocket(m_connection->getSocket());
+
+ if ((resp = m_connection->readResponse())->getCode() != 250)
+ {
+ disconnect();
+
+ throw SMTPCommandError
+ (commands->getLastCommandSent()->getText(), resp->getText(),
+ resp->getCode(), resp->getEnhancedCode());
+ }
+ }
+
+ // Read response for "MAIL" command
+ commands->writeToSocket(m_connection->getSocket());
+
+ if ((resp = m_connection->readResponse())->getCode() != 250)
+ {
+ // SIZE extension: insufficient system storage
+ if (resp->getCode() == 452)
+ {
+ disconnect();
+
+ 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)
+ {
+ disconnect();
+
+ throw SMTPMessageSizeExceedsMaxLimitsException
+ (SMTPCommandError(commands->getLastCommandSent()->getText(), resp->getText(),
+ resp->getCode(), resp->getEnhancedCode()));
+ }
+ // Other error
+ else
+ {
+ disconnect();
+
+ 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());
+
+ resp = m_connection->readResponse();
+
+ if (resp->getCode() != 250 &&
+ resp->getCode() != 251)
+ {
+ // SIZE extension: insufficient system storage
+ if (resp->getCode() == 452)
+ {
+ disconnect();
+
+ 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)
+ {
+ disconnect();
+
+ throw SMTPMessageSizeExceedsMaxLimitsException
+ (SMTPCommandError(commands->getLastCommandSent()->getText(), resp->getText(),
+ resp->getCode(), resp->getEnhancedCode()));
+ }
+ // Other error
+ else
+ {
+ disconnect();
+
+ throw SMTPCommandError
+ (commands->getLastCommandSent()->getText(), resp->getText(),
+ resp->getCode(), resp->getEnhancedCode());
+ }
+ }
+ }
+
+ // Read response for "DATA" command
+ if (sendDATACommand)
+ {
+ commands->writeToSocket(m_connection->getSocket());
+
+ if ((resp = m_connection->readResponse())->getCode() != 354)
+ {
+ disconnect();
+
+ 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)
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ // Send message envelope
+ sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size);
+
+ // 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");
+
+ shared_ptr <SMTPResponse> resp;
+
+ if ((resp = m_connection->readResponse())->getCode() != 250)
+ {
+ disconnect();
+
+ throw SMTPCommandError
+ ("DATA", resp->getText(), resp->getCode(), resp->getEnhancedCode());
+ }
+}
+
+
+void SMTPTransport::send
+ (shared_ptr <vmime::message> msg, const mailbox& expeditor, const mailboxList& recipients,
+ utility::progressListener* progress, const mailbox& sender)
+{
+ 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);
+ return;
+ }
+
+ // Send message envelope
+ sendEnvelope(expeditor, recipients, sender,
+ /* sendDATACommand */ false, msg->getGeneratedSize(ctx));
+
+ // Send the message by chunks
+ SMTPChunkingOutputStreamAdapter chunkStream(m_connection);
+
+ 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/src/vmime/net/smtp/SMTPTransport.hpp b/src/vmime/net/smtp/SMTPTransport.hpp
new file mode 100644
index 00000000..a0f02418
--- /dev/null
+++ b/src/vmime/net/smtp/SMTPTransport.hpp
@@ -0,0 +1,131 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, 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());
+
+ void send
+ (shared_ptr <vmime::message> msg,
+ const mailbox& expeditor,
+ const mailboxList& recipients,
+ utility::progressListener* progress = NULL,
+ const mailbox& sender = mailbox());
+
+ bool isSecuredConnection() const;
+ shared_ptr <connectionInfos> getConnectionInfos() const;
+ shared_ptr <SMTPConnection> getConnection();
+
+ bool isSMTPS() const;
+
+private:
+
+ /** 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)
+ */
+ void sendEnvelope
+ (const mailbox& expeditor,
+ const mailboxList& recipients,
+ const mailbox& sender,
+ bool sendDATACommand,
+ const size_t size);
+
+
+ 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/src/vmime/net/smtp/smtp.hpp b/src/vmime/net/smtp/smtp.hpp
new file mode 100644
index 00000000..2a9ee312
--- /dev/null
+++ b/src/vmime/net/smtp/smtp.hpp
@@ -0,0 +1,33 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/socket.hpp b/src/vmime/net/socket.hpp
new file mode 100644
index 00000000..537c34bb
--- /dev/null
+++ b/src/vmime/net/socket.hpp
@@ -0,0 +1,184 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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"
+
+
+namespace vmime {
+namespace net {
+
+
+/** Interface for connecting to servers.
+ */
+
+class VMIME_EXPORT socket : public object
+{
+public:
+
+ enum Status
+ {
+ STATUS_WOULDBLOCK = 0x1 /**< The receive operation would block. */
+ };
+
+
+ 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;
+
+ /** 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;
+
+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(shared_ptr <timeoutHandler> th) = 0;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_SOCKET_HPP_INCLUDED
diff --git a/src/vmime/net/store.hpp b/src/vmime/net/store.hpp
new file mode 100644
index 00000000..37dcadbc
--- /dev/null
+++ b/src/vmime/net/store.hpp
@@ -0,0 +1,114 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, const serviceInfos& infos, shared_ptr <security::authenticator> auth)
+ : service(sess, infos, 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 { return (TYPE_STORE); }
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_STORE_HPP_INCLUDED
diff --git a/src/vmime/net/timeoutHandler.hpp b/src/vmime/net/timeoutHandler.hpp
new file mode 100644
index 00000000..24129701
--- /dev/null
+++ b/src/vmime/net/timeoutHandler.hpp
@@ -0,0 +1,89 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 time-out in messaging services.
+ */
+
+class VMIME_EXPORT timeoutHandler : public object
+{
+public:
+
+ virtual ~timeoutHandler() { }
+
+ /** Called to test if the time limit has been reached.
+ *
+ * @return true if the time-out delay is elapsed
+ */
+ virtual bool isTimeOut() = 0;
+
+ /** Called to reset the time-out counter.
+ */
+ virtual void resetTimeOut() = 0;
+
+ /** Called when the time limit has been reached (when
+ * isTimeOut() returned true).
+ *
+ * @return true to continue (and reset the time-out)
+ * 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/src/vmime/net/tls/TLSProperties.cpp b/src/vmime/net/tls/TLSProperties.cpp
new file mode 100644
index 00000000..1986db79
--- /dev/null
+++ b/src/vmime/net/tls/TLSProperties.cpp
@@ -0,0 +1,44 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/TLSProperties.hpp b/src/vmime/net/tls/TLSProperties.hpp
new file mode 100644
index 00000000..0dbc8f05
--- /dev/null
+++ b/src/vmime/net/tls/TLSProperties.hpp
@@ -0,0 +1,105 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp b/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp
new file mode 100644
index 00000000..4856e9af
--- /dev/null
+++ b/src/vmime/net/tls/TLSSecuredConnectionInfos.cpp
@@ -0,0 +1,72 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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,
+ shared_ptr <TLSSession> tlsSession, 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/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp b/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp
new file mode 100644
index 00000000..e552d6f9
--- /dev/null
+++ b/src/vmime/net/tls/TLSSecuredConnectionInfos.hpp
@@ -0,0 +1,84 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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,
+ shared_ptr <TLSSession> tlsSession, 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/src/vmime/net/tls/TLSSession.cpp b/src/vmime/net/tls/TLSSession.cpp
new file mode 100644
index 00000000..a46f07ca
--- /dev/null
+++ b/src/vmime/net/tls/TLSSession.cpp
@@ -0,0 +1,48 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/TLSSession.hpp b/src/vmime/net/tls/TLSSession.hpp
new file mode 100644
index 00000000..9e061f89
--- /dev/null
+++ b/src/vmime/net/tls/TLSSession.hpp
@@ -0,0 +1,93 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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:
+
+ /** 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(shared_ptr <security::cert::certificateVerifier> cv, 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(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/src/vmime/net/tls/TLSSocket.cpp b/src/vmime/net/tls/TLSSocket.cpp
new file mode 100644
index 00000000..0419a571
--- /dev/null
+++ b/src/vmime/net/tls/TLSSocket.cpp
@@ -0,0 +1,44 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/TLSSocket.hpp b/src/vmime/net/tls/TLSSocket.hpp
new file mode 100644
index 00000000..e2668ad4
--- /dev/null
+++ b/src/vmime/net/tls/TLSSocket.hpp
@@ -0,0 +1,88 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <TLSSession> session, 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(shared_ptr <timeoutHandler> toHandler = null) = 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() const = 0;
+};
+
+
+} // tls
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT
+
+#endif // VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED
diff --git a/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp b/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp
new file mode 100644
index 00000000..36ab7d7a
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp
@@ -0,0 +1,113 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp b/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp
new file mode 100644
index 00000000..2038778a
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp
@@ -0,0 +1,68 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp b/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp
new file mode 100644
index 00000000..1c520ed1
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp
@@ -0,0 +1,300 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 anonCred;
+ gnutls_certificate_credentials certCred;
+};
+
+static TLSGlobal g_gnutlsGlobal;
+
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+// static
+shared_ptr <TLSSession> TLSSession::create(shared_ptr <security::cert::certificateVerifier> cv, shared_ptr <TLSProperties> props)
+{
+ return make_shared <TLSSession_GnuTLS>(cv, props);
+}
+
+
+TLSSession_GnuTLS::TLSSession_GnuTLS(shared_ptr <security::cert::certificateVerifier> cv, shared_ptr <TLSProperties> props)
+ : m_certVerifier(cv), m_props(props)
+{
+ int res;
+
+ m_gnutlsSession = new gnutls_session;
+
+ 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()
+{
+ if (m_gnutlsSession)
+ {
+ gnutls_deinit(*m_gnutlsSession);
+
+ delete m_gnutlsSession;
+ m_gnutlsSession = NULL;
+ }
+}
+
+
+shared_ptr <TLSSocket> TLSSession_GnuTLS::getSocket(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/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp b/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp
new file mode 100644
index 00000000..7f762b58
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp
@@ -0,0 +1,91 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <security::cert::certificateVerifier> cv, shared_ptr <TLSProperties> props);
+ ~TLSSession_GnuTLS();
+
+
+ shared_ptr <TLSSocket> getSocket(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* 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/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp
new file mode 100644
index 00000000..5a90565b
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp
@@ -0,0 +1,490 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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 "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(shared_ptr <TLSSession> session, shared_ptr <socket> sok)
+{
+ return make_shared <TLSSocket_GnuTLS>
+ (dynamicCast <TLSSession_GnuTLS>(session), sok);
+}
+
+
+TLSSocket_GnuTLS::TLSSocket_GnuTLS(shared_ptr <TLSSession_GnuTLS> session, shared_ptr <socket> sok)
+ : m_session(session), m_wrapped(sok), m_connected(false),
+ m_handshaking(false), m_ex(NULL), m_status(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);
+}
+
+
+TLSSocket_GnuTLS::~TLSSocket_GnuTLS()
+{
+ if (m_ex)
+ {
+ delete m_ex;
+ m_ex = NULL;
+ }
+
+ try
+ {
+ disconnect();
+ }
+ catch (...)
+ {
+ // Don't throw exception in destructor
+ }
+}
+
+
+void TLSSocket_GnuTLS::connect(const string& address, const port_t port)
+{
+ m_wrapped->connect(address, port);
+
+ handshake(null);
+
+ m_connected = true;
+}
+
+
+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();
+}
+
+
+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_WOULDBLOCK;
+
+ const ssize_t ret = gnutls_record_recv
+ (*m_session->m_gnutlsSession,
+ buffer, static_cast <size_t>(count));
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+ 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)
+{
+ ssize_t ret = gnutls_record_send
+ (*m_session->m_gnutlsSession,
+ buffer, static_cast <size_t>(count));
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+ return;
+ }
+
+ TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret));
+ }
+}
+
+
+size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t count)
+{
+ ssize_t ret = gnutls_record_send
+ (*m_session->m_gnutlsSession,
+ buffer, static_cast <size_t>(count));
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+ 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)
+{
+ if (toHandler)
+ toHandler->resetTimeOut();
+
+ // Start handshaking process
+ m_handshaking = true;
+ m_toHandler = toHandler;
+
+ try
+ {
+ while (true)
+ {
+ const int ret = gnutls_handshake(*m_session->m_gnutlsSession);
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED)
+ {
+ // Non-fatal error
+ platform::getHandler()->wait();
+ }
+ else
+ {
+ TLSSession_GnuTLS::throwTLSException("gnutls_handshake", ret);
+ }
+ }
+ else
+ {
+ // Successful handshake
+ break;
+ }
+ }
+ }
+ catch (...)
+ {
+ m_handshaking = false;
+ m_toHandler = null;
+
+ throw;
+ }
+
+ m_handshaking = false;
+ m_toHandler = null;
+
+ // 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;
+}
+
+
+ssize_t TLSSocket_GnuTLS::gnutlsPushFunc
+ (gnutls_transport_ptr trspt, const void* data, size_t len)
+{
+ TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt);
+
+ try
+ {
+ sok->m_wrapped->sendRaw
+ (reinterpret_cast <const byte_t*>(data), len);
+ }
+ catch (exception& e)
+ {
+ // Workaround for bad behaviour when throwing C++ exceptions
+ // from C functions (GNU TLS)
+ sok->m_ex = e.clone();
+ return -1;
+ }
+
+ return len;
+}
+
+
+ssize_t TLSSocket_GnuTLS::gnutlsPullFunc
+ (gnutls_transport_ptr trspt, void* data, size_t len)
+{
+ TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt);
+
+ try
+ {
+ // Workaround for cross-platform asynchronous handshaking:
+ // gnutls_handshake() only returns GNUTLS_E_AGAIN if recv()
+ // returns -1 and errno is set to EGAIN...
+ if (sok->m_handshaking)
+ {
+ while (true)
+ {
+ const ssize_t ret = static_cast <ssize_t>
+ (sok->m_wrapped->receiveRaw
+ (reinterpret_cast <byte_t*>(data), len));
+
+ if (ret == 0)
+ {
+ // No data available yet
+ platform::getHandler()->wait();
+ }
+ else
+ {
+ return ret;
+ }
+
+ // Check whether the time-out delay is elapsed
+ if (sok->m_toHandler && sok->m_toHandler->isTimeOut())
+ {
+ if (!sok->m_toHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+
+ sok->m_toHandler->resetTimeOut();
+ }
+ }
+ }
+ else
+ {
+ const ssize_t n = static_cast <ssize_t>
+ (sok->m_wrapped->receiveRaw
+ (reinterpret_cast <byte_t*>(data), len));
+
+ if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
+ return GNUTLS_E_AGAIN;
+
+ return n;
+ }
+ }
+ catch (exception& e)
+ {
+ // Workaround for bad 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() const
+{
+ unsigned int certCount = 0;
+ const gnutls_datum* rawData = gnutls_certificate_get_peers
+ (*m_session->m_gnutlsSession, &certCount);
+
+ if (rawData == NULL)
+ return null;
+
+ // Try X.509
+ gnutls_x509_crt* x509Certs = new gnutls_x509_crt[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)
+ {
+ // 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);
+ }
+
+ delete [] x509Certs;
+
+ return null;
+}
+
+
+// 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.
+
+#ifndef VMIME_BUILDING_DOC
+
+class TLSSocket_DeleteExWrapper : public object
+{
+public:
+
+ TLSSocket_DeleteExWrapper(exception* ex) : m_ex(ex) { }
+ ~TLSSocket_DeleteExWrapper() { delete m_ex; }
+
+private:
+
+ exception* m_ex;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+void TLSSocket_GnuTLS::internalThrow()
+{
+ static std::vector <shared_ptr <TLSSocket_DeleteExWrapper> > exToDelete;
+
+ if (m_ex)
+ {
+ // Reset the current exception pointer to prevent the same
+ // exception from being thrown again later
+ exception* ex = m_ex;
+ m_ex = NULL;
+
+ // To avoid memory leaks
+ exToDelete.push_back(make_shared <TLSSocket_DeleteExWrapper>(ex));
+
+ throw *ex;
+ }
+}
+
+
+} // tls
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS
diff --git a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp
new file mode 100644
index 00000000..885fac13
--- /dev/null
+++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp
@@ -0,0 +1,120 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <TLSSession_GnuTLS> session, shared_ptr <socket> sok);
+ ~TLSSocket_GnuTLS();
+
+
+ void handshake(shared_ptr <timeoutHandler> toHandler = null);
+
+ shared_ptr <security::cert::certificateChain> getPeerCertificates() const;
+
+ // Implementation of 'socket'
+ void connect(const string& address, const port_t port);
+ void disconnect();
+ bool isConnected() const;
+
+ 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;
+
+private:
+
+ void internalThrow();
+
+#ifdef LIBGNUTLS_VERSION
+ static ssize_t gnutlsPushFunc(gnutls_transport_ptr trspt, const void* data, size_t len);
+ static ssize_t gnutlsPullFunc(gnutls_transport_ptr trspt, void* data, size_t len);
+#else
+ static int gnutlsPushFunc(void* trspt, const void* data, size_t len);
+ static int gnutlsPullFunc(void* trspt, void* data, size_t len);
+#endif // LIBGNUTLS_VERSION
+
+
+ shared_ptr <TLSSession_GnuTLS> m_session;
+ shared_ptr <socket> m_wrapped;
+
+ bool m_connected;
+
+ byte_t m_buffer[65536];
+
+ bool m_handshaking;
+ shared_ptr <timeoutHandler> m_toHandler;
+
+ exception* m_ex;
+
+ unsigned int m_status;
+};
+
+
+} // 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/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp b/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp
new file mode 100644
index 00000000..1bbb9ee5
--- /dev/null
+++ b/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp
@@ -0,0 +1,142 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/utility/sync/autoLock.hpp"
+#include "vmime/utility/sync/criticalSection.hpp"
+
+#include "vmime/platform.hpp"
+
+#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
+
+
+namespace vmime {
+namespace net {
+namespace tls {
+
+
+shared_ptr <vmime::utility::sync::criticalSection >* OpenSSLInitializer::sm_mutexes;
+
+
+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_config(NULL);
+#endif
+
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+
+ unsigned char seed[SEEDSIZE];
+ vmime::platform::getHandler()->generateRandomBytes(seed, SEEDSIZE);
+ RAND_seed(seed, SEEDSIZE);
+
+ int numMutexes = CRYPTO_num_locks();
+ sm_mutexes = new shared_ptr <vmime::utility::sync::criticalSection>[numMutexes];
+
+ for (int i = 0 ; i < numMutexes ; ++i)
+ sm_mutexes[i] = vmime::platform::getHandler()->createCriticalSection();
+
+ CRYPTO_set_locking_callback(&OpenSSLInitializer::lock);
+ CRYPTO_set_id_callback(&OpenSSLInitializer::id);
+}
+
+
+// static
+void OpenSSLInitializer::uninitialize()
+{
+ EVP_cleanup();
+ ERR_free_strings();
+
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_id_callback(NULL);
+
+ delete [] sm_mutexes;
+}
+
+
+// static
+void OpenSSLInitializer::lock(int mode, int n, const char* /* file */, int /* line */)
+{
+ if (mode & CRYPTO_LOCK)
+ sm_mutexes[n]->lock();
+ else
+ sm_mutexes[n]->unlock();
+}
+
+
+// static
+unsigned long OpenSSLInitializer::id()
+{
+ return vmime::platform::getHandler()->getThreadId();
+}
+
+
+} // tls
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL
+
diff --git a/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp b/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp
new file mode 100644
index 00000000..d7595aa8
--- /dev/null
+++ b/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp
@@ -0,0 +1,111 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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
+ */
+ class autoInitializer
+ {
+ public:
+
+ autoInitializer();
+ ~autoInitializer();
+ };
+
+protected:
+
+ class oneTimeInitializer
+ {
+ public:
+
+ oneTimeInitializer();
+ ~oneTimeInitializer();
+ };
+
+
+ /** Initializes the OpenSSL lib
+ */
+ static void initialize();
+
+ /** Shutdown the OpenSSL lib
+ */
+ static void uninitialize();
+
+
+ static shared_ptr <vmime::utility::sync::criticalSection> getMutex();
+
+ enum
+ {
+ SEEDSIZE = 256
+ };
+
+
+ // OpenSSL multithreading support
+ static void lock(int mode, int n, const char* file, int line);
+ static unsigned long id();
+
+private:
+
+ static shared_ptr <vmime::utility::sync::criticalSection >* sm_mutexes;
+};
+
+
+} // 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/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp
new file mode 100644
index 00000000..932477df
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp
@@ -0,0 +1,112 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp b/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp
new file mode 100644
index 00000000..5d2f075a
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp
@@ -0,0 +1,68 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp
new file mode 100644
index 00000000..cf600a63
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp
@@ -0,0 +1,135 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/exception.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(shared_ptr <security::cert::certificateVerifier> cv, shared_ptr <TLSProperties> props)
+{
+ return make_shared <TLSSession_OpenSSL>(cv, props);
+}
+
+
+TLSSession_OpenSSL::TLSSession_OpenSSL(shared_ptr <vmime::security::cert::certificateVerifier> cv, 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(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)
+{
+ 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 exceptions::certificate_exception(oss.str());
+ }
+}
+
+
+void TLSSession_OpenSSL::useCertificateChainFile(const vmime::string& chainFile)
+{
+ 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 exceptions::certificate_exception(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/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp b/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp
new file mode 100644
index 00000000..5a2b60a8
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp
@@ -0,0 +1,108 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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, shared_ptr <TLSProperties> props);
+ ~TLSSession_OpenSSL();
+
+
+ shared_ptr <TLSSocket> getSocket(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
+ * @param passwd_callback If the private key is stored encrypted the
+ */
+ 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/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp
new file mode 100644
index 00000000..ef6647d6
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp
@@ -0,0 +1,601 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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;
+
+
+// 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
+};
+
+
+// static
+shared_ptr <TLSSocket> TLSSocket::wrap(shared_ptr <TLSSession> session, shared_ptr <socket> sok)
+{
+ return make_shared <TLSSocket_OpenSSL>
+ (dynamicCast <TLSSession_OpenSSL>(session), sok);
+}
+
+
+TLSSocket_OpenSSL::TLSSocket_OpenSSL(shared_ptr <TLSSession_OpenSSL> session, shared_ptr <socket> sok)
+ : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0), m_status(0), m_ex(NULL)
+{
+}
+
+
+TLSSocket_OpenSSL::~TLSSocket_OpenSSL()
+{
+ try
+ {
+ disconnect();
+
+ if (m_ssl)
+ {
+ SSL_free(m_ssl);
+ m_ssl = 0;
+ }
+ }
+ catch (...)
+ {
+ // Don't throw in destructor
+ }
+}
+
+
+void TLSSocket_OpenSSL::createSSLHandle()
+{
+ if (m_wrapped->isConnected())
+ {
+ BIO* sockBio = BIO_new(&sm_customBIOMethod);
+ sockBio->ptr = this;
+ sockBio->init = 1;
+
+ 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_connect_state(m_ssl);
+ }
+ else
+ {
+ throw exceptions::tls_exception("Unconnected socket error");
+ }
+}
+
+
+void TLSSocket_OpenSSL::connect(const string& address, const port_t port)
+{
+ m_wrapped->connect(address, port);
+
+ createSSLHandle();
+
+ handshake(null);
+
+ m_connected = true;
+}
+
+
+void TLSSocket_OpenSSL::disconnect()
+{
+ if (m_connected)
+ {
+ 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);
+ }
+
+ m_wrapped->disconnect();
+ m_connected = false;
+ }
+}
+
+
+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();
+}
+
+
+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)
+{
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ 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 || error == SSL_ERROR_WANT_READ)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+ return 0;
+ }
+
+ handleError(rc);
+ }
+
+ return rc;
+}
+
+
+void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count)
+{
+ sendRawNonBlocking(buffer, count);
+}
+
+
+size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count)
+{
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ 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 || error == SSL_ERROR_WANT_READ)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+ return 0;
+ }
+
+ handleError(rc);
+ }
+
+ return rc;
+}
+
+
+void TLSSocket_OpenSSL::handshake(shared_ptr <timeoutHandler> toHandler)
+{
+ if (toHandler)
+ toHandler->resetTimeOut();
+
+ // Start handshaking process
+ m_toHandler = toHandler;
+
+ if (!m_ssl)
+ createSSLHandle();
+
+ try
+ {
+ int rc;
+
+ while ((rc = SSL_do_handshake(m_ssl)) <= 0)
+ {
+ const int err = SSL_get_error(m_ssl, rc);
+
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
+ {
+ // No data available yet
+ platform::getHandler()->wait();
+ }
+ else
+ {
+ handleError(rc);
+ }
+
+ // Check whether the time-out delay is elapsed
+ if (m_toHandler && m_toHandler->isTimeOut())
+ {
+ if (!m_toHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+
+ m_toHandler->resetTimeOut();
+ }
+ }
+ }
+ catch (...)
+ {
+ SSL_free(m_ssl);
+ m_ssl = 0;
+ m_toHandler = null;
+ throw;
+ }
+
+ m_toHandler = null;
+
+ // 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;
+}
+
+
+shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates() const
+{
+ 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:
+ return;
+
+ case SSL_ERROR_SYSCALL:
+ {
+ if (lastError == 0)
+ {
+ if (rc == 0)
+ {
+ throw exceptions::tls_exception("SSL connection unexpectedly closed");
+ }
+ else
+ {
+ vmime::string msg;
+ std::ostringstream oss(msg);
+ 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->ptr);
+
+ if (!bio->init || !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>(len);
+ }
+ 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->ptr);
+
+ if (!bio->init || !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->shutdown;
+ break;
+
+ case BIO_CTRL_SET_CLOSE:
+
+ bio->shutdown = 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->init = 0;
+ bio->num = 0;
+ bio->ptr = NULL;
+ bio->flags = 0;
+
+ return 1;
+}
+
+
+// static
+int TLSSocket_OpenSSL::bio_destroy(BIO* bio)
+{
+ if (bio == NULL)
+ return 0;
+
+ if (bio->shutdown)
+ {
+ bio->ptr = NULL;
+ bio->init = 0;
+ bio->flags = 0;
+ }
+
+ return 1;
+}
+
+
+} // tls
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL
diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp
new file mode 100644
index 00000000..410fffcf
--- /dev/null
+++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp
@@ -0,0 +1,132 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <TLSSession_OpenSSL> session, shared_ptr <socket> sok);
+ ~TLSSocket_OpenSSL();
+
+
+ void handshake(shared_ptr <timeoutHandler> toHandler = null);
+
+ shared_ptr <security::cert::certificateChain> getPeerCertificates() const;
+
+ // Implementation of 'socket'
+ void connect(const string& address, const port_t port);
+ void disconnect();
+ bool isConnected() const;
+
+ 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;
+
+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];
+
+ shared_ptr <timeoutHandler> m_toHandler;
+
+ SSL* m_ssl;
+
+ unsigned long m_status;
+
+ // Last exception thrown from C BIO functions
+ std::auto_ptr <std::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/src/vmime/net/transport.cpp b/src/vmime/net/transport.cpp
new file mode 100644
index 00000000..dd7281d0
--- /dev/null
+++ b/src/vmime/net/transport.cpp
@@ -0,0 +1,236 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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(shared_ptr <session> sess, const serviceInfos& infos, shared_ptr <security::authenticator> auth)
+ : service(sess, infos, auth)
+{
+}
+
+
+shared_ptr <headerField> transport::processHeaderField(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(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 != NULL)
+ recipients.appendMailbox(mbox);
+ }
+}
+
+
+void transport::send(shared_ptr <vmime::message> msg, utility::progressListener* progress)
+{
+ // 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(shared_ptr <vmime::message> _msg,
+ 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);
+}
+
+
+void transport::send
+ (shared_ptr <vmime::message> msg, const mailbox& expeditor, const mailboxList& recipients,
+ utility::progressListener* progress, const mailbox& sender)
+{
+ // 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);
+}
+
+
+transport::Type transport::getType() const
+{
+ return (TYPE_TRANSPORT);
+}
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
diff --git a/src/vmime/net/transport.hpp b/src/vmime/net/transport.hpp
new file mode 100644
index 00000000..6c405cbb
--- /dev/null
+++ b/src/vmime/net/transport.hpp
@@ -0,0 +1,137 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2013 Vincent Richard <[email protected]>
+//
+// 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/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(shared_ptr <session> sess, const serviceInfos& infos, 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
+ */
+ virtual void send(shared_ptr <vmime::message> msg, utility::progressListener* progress = NULL);
+
+ /** 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)
+ */
+ virtual void send
+ (const mailbox& expeditor,
+ const mailboxList& recipients,
+ utility::inputStream& is,
+ const size_t size,
+ utility::progressListener* progress = NULL,
+ const mailbox& sender = mailbox()) = 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)
+ */
+ virtual void send
+ (shared_ptr <vmime::message> msg,
+ const mailbox& expeditor,
+ const mailboxList& recipients,
+ utility::progressListener* progress = NULL,
+ const mailbox& sender = mailbox());
+
+
+ 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(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(shared_ptr <header> header);
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_HAVE_MESSAGING_FEATURES
+
+#endif // VMIME_NET_TRANSPORT_HPP_INCLUDED