aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2005-08-23 19:11:19 +0000
committerVincent Richard <[email protected]>2005-08-23 19:11:19 +0000
commit28bafee9444aec42a92f250833a64f4f3acf25cb (patch)
tree6452d6eab320cea4a68c5dcb4605e698347de1ec
parentAdded test case for '?' in the middle of the encoded buffer. (diff)
downloadvmime-28bafee9444aec42a92f250833a64f4f3acf25cb.tar.gz
vmime-28bafee9444aec42a92f250833a64f4f3acf25cb.zip
Renamed 'vmime::messaging' to 'vmime::net'.
-rw-r--r--ChangeLog6
-rw-r--r--SConstruct68
-rw-r--r--examples/example6.cpp70
-rw-r--r--examples/example7.cpp20
-rw-r--r--src/base.cpp4
-rw-r--r--src/exception.cpp50
-rw-r--r--src/net/authHelper.cpp105
-rw-r--r--src/net/authenticationInfos.cpp52
-rw-r--r--src/net/authenticator.cpp33
-rw-r--r--src/net/builtinServices.inl56
-rw-r--r--src/net/defaultAuthenticator.cpp43
-rw-r--r--src/net/events.cpp111
-rw-r--r--src/net/folder.cpp96
-rw-r--r--src/net/imap/IMAPConnection.cpp313
-rw-r--r--src/net/imap/IMAPFolder.cpp1608
-rw-r--r--src/net/imap/IMAPMessage.cpp859
-rw-r--r--src/net/imap/IMAPStore.cpp308
-rw-r--r--src/net/imap/IMAPTag.cpp99
-rw-r--r--src/net/imap/IMAPUtils.cpp555
-rw-r--r--src/net/maildir/maildirFolder.cpp1387
-rw-r--r--src/net/maildir/maildirMessage.cpp505
-rw-r--r--src/net/maildir/maildirStore.cpp250
-rw-r--r--src/net/maildir/maildirUtils.cpp208
-rw-r--r--src/net/message.cpp46
-rw-r--r--src/net/pop3/POP3Folder.cpp826
-rw-r--r--src/net/pop3/POP3Message.cpp213
-rw-r--r--src/net/pop3/POP3Store.cpp630
-rw-r--r--src/net/sendmail/sendmailTransport.cpp222
-rw-r--r--src/net/service.cpp70
-rw-r--r--src/net/serviceFactory.cpp124
-rw-r--r--src/net/serviceInfos.cpp150
-rw-r--r--src/net/session.cpp126
-rw-r--r--src/net/simpleAuthenticator.cpp69
-rw-r--r--src/net/smtp/SMTPTransport.cpp503
-rw-r--r--src/net/transport.cpp116
-rw-r--r--src/platforms/posix/posixHandler.cpp4
-rw-r--r--src/platforms/posix/posixSocket.cpp2
-rw-r--r--src/platforms/windows/windowsHandler.cpp4
-rw-r--r--src/platforms/windows/windowsSocket.cpp2
-rw-r--r--src/utility/stream.cpp6
-rw-r--r--vmime/exception.hpp54
-rw-r--r--vmime/net/authHelper.hpp38
-rw-r--r--vmime/net/authenticationInfos.hpp64
-rw-r--r--vmime/net/authenticator.hpp54
-rw-r--r--vmime/net/defaultAuthenticator.hpp60
-rw-r--r--vmime/net/events.hpp229
-rw-r--r--vmime/net/folder.hpp379
-rw-r--r--vmime/net/imap/IMAPConnection.hpp116
-rw-r--r--vmime/net/imap/IMAPFolder.hpp158
-rw-r--r--vmime/net/imap/IMAPMessage.hpp111
-rw-r--r--vmime/net/imap/IMAPParser.hpp5082
-rw-r--r--vmime/net/imap/IMAPStore.hpp137
-rw-r--r--vmime/net/imap/IMAPTag.hpp66
-rw-r--r--vmime/net/imap/IMAPUtils.hpp68
-rw-r--r--vmime/net/maildir/maildirFolder.hpp178
-rw-r--r--vmime/net/maildir/maildirMessage.hpp103
-rw-r--r--vmime/net/maildir/maildirStore.hpp114
-rw-r--r--vmime/net/maildir/maildirUtils.hpp160
-rw-r--r--vmime/net/message.hpp292
-rw-r--r--vmime/net/pop3/POP3Folder.hpp148
-rw-r--r--vmime/net/pop3/POP3Message.hpp98
-rw-r--r--vmime/net/pop3/POP3Store.hpp138
-rw-r--r--vmime/net/sendmail/sendmailTransport.hpp103
-rw-r--r--vmime/net/service.hpp161
-rw-r--r--vmime/net/serviceFactory.hpp188
-rw-r--r--vmime/net/serviceInfos.hpp228
-rw-r--r--vmime/net/session.hpp135
-rw-r--r--vmime/net/simpleAuthenticator.hpp62
-rw-r--r--vmime/net/smtp/SMTPTransport.hpp113
-rw-r--r--vmime/net/socket.hpp112
-rw-r--r--vmime/net/store.hpp102
-rw-r--r--vmime/net/timeoutHandler.hpp77
-rw-r--r--vmime/net/transport.hpp75
-rw-r--r--vmime/platformDependant.hpp8
-rw-r--r--vmime/platforms/posix/posixHandler.hpp4
-rw-r--r--vmime/platforms/posix/posixSocket.hpp8
-rw-r--r--vmime/platforms/windows/windowsHandler.hpp4
-rw-r--r--vmime/platforms/windows/windowsSocket.hpp8
-rw-r--r--vmime/types.hpp4
-rw-r--r--vmime/utility/stream.hpp12
-rw-r--r--vmime/vmime.hpp20
81 files changed, 19019 insertions, 171 deletions
diff --git a/ChangeLog b/ChangeLog
index 8a66f1a0..988059f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,12 @@
VERSION 0.7.2cvs
================
+2005-08-23 Vincent Richard <[email protected]>
+
+ * All sources: renamed 'vmime::messaging' to 'vmime::net'. An alias has been
+ kept for compatibility with previous versions (its use should be considered
+ as deprecated).
+
2005-08-19 Vincent Richard <[email protected]>
* exception.hpp: vmime::exception now inherits from std::exception.
diff --git a/SConstruct b/SConstruct
index 3f814709..156eb1c4 100644
--- a/SConstruct
+++ b/SConstruct
@@ -58,7 +58,7 @@ packageAPIAge = packageVersionMinor
packageName = 'libvmime'
packageGenericName = 'vmime'
packageRealName = 'VMime Library'
-packageDescription = 'VMime C++ Mail Library (http://vmime.sourceforge.net)'
+packageDescription = 'VMime C++ Mail Library (http://www.vmime.org)'
packageMaintainer = '[email protected]'
packageVersion = '%d.%d.%d' % (packageVersionMajor, packageVersionMinor, packageVersionMicro)
@@ -182,65 +182,65 @@ libvmime_examples_sources = [
]
libvmime_messaging_sources = [
- 'messaging/authenticator.cpp', 'messaging/authenticator.hpp',
- 'messaging/authenticationInfos.cpp', 'messaging/authenticationInfos.hpp',
- 'messaging/authHelper.cpp', 'messaging/authHelper.hpp',
- 'messaging/builtinServices.inl',
- 'messaging/defaultAuthenticator.cpp', 'messaging/defaultAuthenticator.hpp',
- 'messaging/events.cpp', 'messaging/events.hpp',
- 'messaging/folder.cpp', 'messaging/folder.hpp',
- 'messaging/message.cpp', 'messaging/message.hpp',
- 'messaging/service.cpp', 'messaging/service.hpp',
- 'messaging/serviceFactory.cpp', 'messaging/serviceFactory.hpp',
- 'messaging/serviceInfos.cpp', 'messaging/serviceInfos.hpp',
- 'messaging/session.cpp', 'messaging/session.hpp',
- 'messaging/simpleAuthenticator.cpp', 'messaging/simpleAuthenticator.hpp',
- 'messaging/socket.hpp',
- 'messaging/store.hpp',
- 'messaging/timeoutHandler.hpp',
- 'messaging/transport.cpp', 'messaging/transport.hpp'
+ 'net/authenticator.cpp', 'net/authenticator.hpp',
+ 'net/authenticationInfos.cpp', 'net/authenticationInfos.hpp',
+ 'net/authHelper.cpp', 'net/authHelper.hpp',
+ 'net/builtinServices.inl',
+ 'net/defaultAuthenticator.cpp', 'net/defaultAuthenticator.hpp',
+ 'net/events.cpp', 'net/events.hpp',
+ 'net/folder.cpp', 'net/folder.hpp',
+ 'net/message.cpp', 'net/message.hpp',
+ 'net/service.cpp', 'net/service.hpp',
+ 'net/serviceFactory.cpp', 'net/serviceFactory.hpp',
+ 'net/serviceInfos.cpp', 'net/serviceInfos.hpp',
+ 'net/session.cpp', 'net/session.hpp',
+ 'net/simpleAuthenticator.cpp', 'net/simpleAuthenticator.hpp',
+ 'net/socket.hpp',
+ 'net/store.hpp',
+ 'net/timeoutHandler.hpp',
+ 'net/transport.cpp', 'net/transport.hpp'
]
libvmime_messaging_proto_sources = [
[
'pop3',
[
- 'messaging/pop3/POP3Store.cpp', 'messaging/pop3/POP3Store.hpp',
- 'messaging/pop3/POP3Folder.cpp', 'messaging/pop3/POP3Folder.hpp',
- 'messaging/pop3/POP3Message.cpp', 'messaging/pop3/POP3Message.hpp'
+ 'net/pop3/POP3Store.cpp', 'net/pop3/POP3Store.hpp',
+ 'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp',
+ 'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.hpp'
]
],
[
'smtp',
[
- 'messaging/smtp/SMTPTransport.cpp', 'messaging/smtp/SMTPTransport.hpp'
+ 'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp'
]
],
[
'imap',
[
- 'messaging/imap/IMAPConnection.cpp', 'messaging/imap/IMAPConnection.hpp',
- 'messaging/imap/IMAPStore.cpp', 'messaging/imap/IMAPStore.hpp',
- 'messaging/imap/IMAPFolder.cpp', 'messaging/imap/IMAPFolder.hpp',
- 'messaging/imap/IMAPMessage.cpp', 'messaging/imap/IMAPMessage.hpp',
- 'messaging/imap/IMAPTag.cpp', 'messaging/imap/IMAPTag.hpp',
- 'messaging/imap/IMAPUtils.cpp', 'messaging/imap/IMAPUtils.hpp',
- 'messaging/imap/IMAPParser.hpp'
+ 'net/imap/IMAPConnection.cpp', 'net/imap/IMAPConnection.hpp',
+ 'net/imap/IMAPStore.cpp', 'net/imap/IMAPStore.hpp',
+ 'net/imap/IMAPFolder.cpp', 'net/imap/IMAPFolder.hpp',
+ 'net/imap/IMAPMessage.cpp', 'net/imap/IMAPMessage.hpp',
+ 'net/imap/IMAPTag.cpp', 'net/imap/IMAPTag.hpp',
+ 'net/imap/IMAPUtils.cpp', 'net/imap/IMAPUtils.hpp',
+ 'net/imap/IMAPParser.hpp'
]
],
[
'maildir',
[
- 'messaging/maildir/maildirStore.cpp', 'messaging/maildir/maildirStore.hpp',
- 'messaging/maildir/maildirFolder.cpp', 'messaging/maildir/maildirFolder.hpp',
- 'messaging/maildir/maildirMessage.cpp', 'messaging/maildir/maildirMessage.hpp',
- 'messaging/maildir/maildirUtils.cpp', 'messaging/maildir/maildirUtils.hpp'
+ 'net/maildir/maildirStore.cpp', 'net/maildir/maildirStore.hpp',
+ 'net/maildir/maildirFolder.cpp', 'net/maildir/maildirFolder.hpp',
+ 'net/maildir/maildirMessage.cpp', 'net/maildir/maildirMessage.hpp',
+ 'net/maildir/maildirUtils.cpp', 'net/maildir/maildirUtils.hpp'
]
],
[
'sendmail',
[
- 'messaging/sendmail/sendmailTransport.cpp', 'messaging/sendmail/sendmailTransport.hpp'
+ 'net/sendmail/sendmailTransport.cpp', 'net/sendmail/sendmailTransport.hpp'
]
]
]
diff --git a/examples/example6.cpp b/examples/example6.cpp
index eec80d72..704a5353 100644
--- a/examples/example6.cpp
+++ b/examples/example6.cpp
@@ -26,14 +26,14 @@
// Global session object
-static vmime::ref <vmime::messaging::session> g_session
- = vmime::create <vmime::messaging::session>();
+static vmime::ref <vmime::net::session> g_session
+ = vmime::create <vmime::net::session>();
// Authentification handler
-class interactiveAuthenticator : public vmime::messaging::authenticator
+class interactiveAuthenticator : public vmime::net::authenticator
{
- const vmime::messaging::authenticationInfos requestAuthInfos() const
+ const vmime::net::authenticationInfos requestAuthInfos() const
{
vmime::string username, password;
@@ -50,7 +50,7 @@ class interactiveAuthenticator : public vmime::messaging::authenticator
std::getline(std::cin, password);
- return (vmime::messaging::authenticationInfos(username, password));
+ return (vmime::net::authenticationInfos(username, password));
}
};
@@ -108,11 +108,11 @@ static std::ostream& operator<<(std::ostream& os, const vmime::exception& e)
* @param s structure object
* @param level current depth
*/
-static void printStructure(const vmime::messaging::structure& s, const int level = 0)
+static void printStructure(const vmime::net::structure& s, const int level = 0)
{
for (int i = 1 ; i <= s.getCount() ; ++i)
{
- const vmime::messaging::part& part = s[i];
+ const vmime::net::part& part = s[i];
for (int j = 0 ; j < level * 2 ; ++j)
std::cout << " ";
@@ -127,7 +127,7 @@ static void printStructure(const vmime::messaging::structure& s, const int level
}
-static const vmime::string getFolderPathString(vmime::ref <vmime::messaging::folder> f)
+static const vmime::string getFolderPathString(vmime::ref <vmime::net::folder> f)
{
const vmime::string n = f->getName().getBuffer();
@@ -137,7 +137,7 @@ static const vmime::string getFolderPathString(vmime::ref <vmime::messaging::fol
}
else
{
- vmime::ref <vmime::messaging::folder> p = f->getParent();
+ vmime::ref <vmime::net::folder> p = f->getParent();
return getFolderPathString(p) + n + "/";
}
}
@@ -147,14 +147,14 @@ static const vmime::string getFolderPathString(vmime::ref <vmime::messaging::fol
*
* @param folder current folder
*/
-static void printFolders(vmime::ref <vmime::messaging::folder> folder, const int level = 0)
+static void printFolders(vmime::ref <vmime::net::folder> folder, const int level = 0)
{
for (int j = 0 ; j < level * 2 ; ++j)
std::cout << " ";
std::cout << getFolderPathString(folder) << std::endl;
- std::vector <vmime::ref <vmime::messaging::folder> > subFolders = folder->getFolders(false);
+ std::vector <vmime::ref <vmime::net::folder> > subFolders = folder->getFolders(false);
for (unsigned int i = 0 ; i < subFolders.size() ; ++i)
printFolders(subFolders[i], level + 1);
@@ -210,7 +210,7 @@ static void sendMessage()
vmime::utility::url url(urlString);
- vmime::ref <vmime::messaging::transport> tr =
+ vmime::ref <vmime::net::transport> tr =
g_session->getTransport(url, vmime::create <interactiveAuthenticator>());
// You can also set some properties (see example7 to know the properties
@@ -307,7 +307,7 @@ static void connectStore()
// If no authenticator is given in argument to getStore(), a default one
// is used. Its behaviour is to get the user credentials from the
// session properties "auth.username" and "auth.password".
- vmime::ref <vmime::messaging::store> st;
+ vmime::ref <vmime::net::store> st;
if (url.getUsername().empty() || url.getPassword().empty())
st = g_session->getStore(url, vmime::create <interactiveAuthenticator>());
@@ -318,10 +318,10 @@ static void connectStore()
st->connect();
// Open the default folder in this store
- vmime::ref <vmime::messaging::folder> f = st->getDefaultFolder();
-// vmime::ref <vmime::messaging::folder> f = st->getFolder(vmime::utility::path("a"));
+ vmime::ref <vmime::net::folder> f = st->getDefaultFolder();
+// vmime::ref <vmime::net::folder> f = st->getFolder(vmime::utility::path("a"));
- f->open(vmime::messaging::folder::MODE_READ_WRITE);
+ f->open(vmime::net::folder::MODE_READ_WRITE);
int count = f->getMessageCount();
@@ -330,7 +330,7 @@ static void connectStore()
for (bool cont = true ; cont ; )
{
- typedef std::map <int, vmime::ref <vmime::messaging::message> > MessageList;
+ typedef std::map <int, vmime::ref <vmime::net::message> > MessageList;
MessageList msgList;
try
@@ -348,7 +348,7 @@ static void connectStore()
const int choice = printMenu(choices);
// Request message number
- vmime::ref <vmime::messaging::message> msg;
+ vmime::ref <vmime::net::message> msg;
if (choice != 6 && choice != 7)
{
@@ -389,19 +389,19 @@ static void connectStore()
// Show message flags
case 1:
- f->fetchMessage(msg, vmime::messaging::folder::FETCH_FLAGS);
+ f->fetchMessage(msg, vmime::net::folder::FETCH_FLAGS);
- if (msg->getFlags() & vmime::messaging::message::FLAG_SEEN)
+ if (msg->getFlags() & vmime::net::message::FLAG_SEEN)
std::cout << "FLAG_SEEN" << std::endl;
- if (msg->getFlags() & vmime::messaging::message::FLAG_RECENT)
+ if (msg->getFlags() & vmime::net::message::FLAG_RECENT)
std::cout << "FLAG_RECENT" << std::endl;
- if (msg->getFlags() & vmime::messaging::message::FLAG_REPLIED)
+ if (msg->getFlags() & vmime::net::message::FLAG_REPLIED)
std::cout << "FLAG_REPLIED" << std::endl;
- if (msg->getFlags() & vmime::messaging::message::FLAG_DELETED)
+ if (msg->getFlags() & vmime::net::message::FLAG_DELETED)
std::cout << "FLAG_DELETED" << std::endl;
- if (msg->getFlags() & vmime::messaging::message::FLAG_MARKED)
+ if (msg->getFlags() & vmime::net::message::FLAG_MARKED)
std::cout << "FLAG_MARKED" << std::endl;
- if (msg->getFlags() & vmime::messaging::message::FLAG_PASSED)
+ if (msg->getFlags() & vmime::net::message::FLAG_PASSED)
std::cout << "FLAG_PASSED" << std::endl;
break;
@@ -409,21 +409,21 @@ static void connectStore()
// Show message structure
case 2:
- f->fetchMessage(msg, vmime::messaging::folder::FETCH_STRUCTURE);
+ f->fetchMessage(msg, vmime::net::folder::FETCH_STRUCTURE);
printStructure(msg->getStructure());
break;
// Show message header
case 3:
- f->fetchMessage(msg, vmime::messaging::folder::FETCH_FULL_HEADER);
+ f->fetchMessage(msg, vmime::net::folder::FETCH_FULL_HEADER);
std::cout << msg->getHeader()->generate() << std::endl;
break;
// Show message envelope
case 4:
- f->fetchMessage(msg, vmime::messaging::folder::FETCH_ENVELOPE);
+ f->fetchMessage(msg, vmime::net::folder::FETCH_ENVELOPE);
#define ENV_HELPER(x) \
try { std::cout << msg->getHeader()->x()->generate() << std::endl; } \
@@ -449,7 +449,7 @@ static void connectStore()
// List folders
case 6:
{
- vmime::ref <vmime::messaging::folder>
+ vmime::ref <vmime::net::folder>
root = st->getRootFolder();
printFolders(root);
@@ -477,19 +477,19 @@ static void connectStore()
// Folder renaming
{
- vmime::ref <vmime::messaging::folder> f = st->getFolder(vmime::messaging::folder::path("c"));
- f->rename(vmime::messaging::folder::path("c2"));
+ vmime::ref <vmime::net::folder> f = st->getFolder(vmime::net::folder::path("c"));
+ f->rename(vmime::net::folder::path("c2"));
- vmime::ref <vmime::messaging::folder> g = st->getFolder(vmime::messaging::folder::path("c2"));
- g->rename(vmime::messaging::folder::path("c"));
+ vmime::ref <vmime::net::folder> g = st->getFolder(vmime::net::folder::path("c2"));
+ g->rename(vmime::net::folder::path("c"));
}
// Message copy: copy all messages from 'f' to 'g'
{
- vmime::ref <vmime::messaging::folder> g = st->getFolder(vmime::messaging::folder::path("TEMP"));
+ vmime::ref <vmime::net::folder> g = st->getFolder(vmime::net::folder::path("TEMP"));
if (!g->exists())
- g->create(vmime::messaging::folder::TYPE_CONTAINS_MESSAGES);
+ g->create(vmime::net::folder::TYPE_CONTAINS_MESSAGES);
f->copyMessages(g->getFullPath());
}
diff --git a/examples/example7.cpp b/examples/example7.cpp
index 7161f4e6..8e3e0464 100644
--- a/examples/example7.cpp
+++ b/examples/example7.cpp
@@ -60,23 +60,23 @@ int main()
std::cout << std::endl;
// Enumerate messaging services and their properties
- vmime::messaging::serviceFactory* sf = vmime::messaging::serviceFactory::getInstance();
+ vmime::net::serviceFactory* sf = vmime::net::serviceFactory::getInstance();
std::cout << "Available messaging services:" << std::endl;
for (int i = 0 ; i < sf->getServiceCount() ; ++i)
{
- const vmime::messaging::serviceFactory::registeredService& serv = *sf->getServiceAt(i);
+ const vmime::net::serviceFactory::registeredService& serv = *sf->getServiceAt(i);
std::cout << " * " << serv.getName() << std::endl;
- std::vector <vmime::messaging::serviceInfos::property> props =
+ std::vector <vmime::net::serviceInfos::property> props =
serv.getInfos().getAvailableProperties();
- for (std::vector <vmime::messaging::serviceInfos::property>::const_iterator it = props.begin() ;
+ for (std::vector <vmime::net::serviceInfos::property>::const_iterator it = props.begin() ;
it != props.end() ; ++it)
{
- const vmime::messaging::serviceInfos::property& p = *it;
+ const vmime::net::serviceInfos::property& p = *it;
const vmime::string name = serv.getInfos().getPropertyPrefix() + p.getName();
@@ -84,17 +84,17 @@ int main()
switch (p.getType())
{
- case vmime::messaging::serviceInfos::property::TYPE_INTEGER: type = "TYPE_INTEGER"; break;
- case vmime::messaging::serviceInfos::property::TYPE_STRING: type = "TYPE_STRING"; break;
- case vmime::messaging::serviceInfos::property::TYPE_BOOL: type = "TYPE_BOOL"; break;
+ case vmime::net::serviceInfos::property::TYPE_INTEGER: type = "TYPE_INTEGER"; break;
+ case vmime::net::serviceInfos::property::TYPE_STRING: type = "TYPE_STRING"; break;
+ case vmime::net::serviceInfos::property::TYPE_BOOL: type = "TYPE_BOOL"; break;
default: type = "(unknown)"; break;
}
vmime::string flags;
- if (p.getFlags() & vmime::messaging::serviceInfos::property::FLAG_REQUIRED)
+ if (p.getFlags() & vmime::net::serviceInfos::property::FLAG_REQUIRED)
flags += " FLAG_REQUIRED";
- if (p.getFlags() & vmime::messaging::serviceInfos::property::FLAG_HIDDEN)
+ if (p.getFlags() & vmime::net::serviceInfos::property::FLAG_HIDDEN)
flags += " FLAG_HIDDEN";
std::cout << " - " << serv.getInfos().getPropertyPrefix() + p.getName();
diff --git a/src/base.cpp b/src/base.cpp
index 8ccd1207..217b1fa5 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -40,7 +40,7 @@
#include "vmime/options.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
- #include "vmime/messaging/serviceFactory.hpp"
+ #include "vmime/net/serviceFactory.hpp"
#endif
@@ -143,7 +143,7 @@ public:
textPartFactory::getInstance();
#if VMIME_HAVE_MESSAGING_FEATURES
- messaging::serviceFactory::getInstance();
+ net::serviceFactory::getInstance();
#endif
}
};
diff --git a/src/exception.cpp b/src/exception.cpp
index 957b126c..5090e4fe 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -317,15 +317,15 @@ const char* system_error::name() const throw() { return "system_error"; }
//
-// messaging_exception
+// net_exception
//
-messaging_exception::~messaging_exception() throw() {}
-messaging_exception::messaging_exception(const string& what, const exception& other)
+net_exception::~net_exception() throw() {}
+net_exception::net_exception(const string& what, const exception& other)
: exception(what, other) {}
-exception* messaging_exception::clone() const { return new messaging_exception(*this); }
-const char* messaging_exception::name() const throw() { return "messaging_exception"; }
+exception* net_exception::clone() const { return new net_exception(*this); }
+const char* net_exception::name() const throw() { return "net_exception"; }
//
@@ -334,7 +334,7 @@ const char* messaging_exception::name() const throw() { return "messaging_except
connection_error::~connection_error() throw() {}
connection_error::connection_error(const string& what, const exception& other)
- : messaging_exception(what.empty()
+ : net_exception(what.empty()
? "Connection error."
: "Connection error: '" + what + "'.", other) {}
@@ -348,7 +348,7 @@ const char* connection_error::name() const throw() { return "connection_error";
connection_greeting_error::~connection_greeting_error() throw() {}
connection_greeting_error::connection_greeting_error(const string& response, const exception& other)
- : messaging_exception("Greeting error.", other), m_response(response) {}
+ : net_exception("Greeting error.", other), m_response(response) {}
const string& connection_greeting_error::response() const { return (m_response); }
@@ -362,7 +362,7 @@ const char* connection_greeting_error::name() const throw() { return "connection
authentication_error::~authentication_error() throw() {}
authentication_error::authentication_error(const string& response, const exception& other)
- : messaging_exception("Authentication error.", other), m_response(response) {}
+ : net_exception("Authentication error.", other), m_response(response) {}
const string& authentication_error::response() const { return (m_response); }
@@ -376,7 +376,7 @@ const char* authentication_error::name() const throw() { return "authentication_
unsupported_option::~unsupported_option() throw() {}
unsupported_option::unsupported_option(const exception& other)
- : messaging_exception("Unsupported option.", other) {}
+ : net_exception("Unsupported option.", other) {}
exception* unsupported_option::clone() const { return new unsupported_option(*this); }
const char* unsupported_option::name() const throw() { return "unsupported_option"; }
@@ -388,7 +388,7 @@ const char* unsupported_option::name() const throw() { return "unsupported_optio
no_service_available::~no_service_available() throw() {}
no_service_available::no_service_available(const string& proto, const exception& other)
- : messaging_exception(proto.empty()
+ : net_exception(proto.empty()
? "No service available for this protocol."
: "No service available for this protocol: '" + proto + "'.", other) {}
@@ -402,7 +402,7 @@ const char* no_service_available::name() const throw() { return "no_service_avai
illegal_state::~illegal_state() throw() {}
illegal_state::illegal_state(const string& state, const exception& other)
- : messaging_exception("Illegal state to accomplish the operation: '" + state + "'.", other) {}
+ : net_exception("Illegal state to accomplish the operation: '" + state + "'.", other) {}
exception* illegal_state::clone() const { return new illegal_state(*this); }
const char* illegal_state::name() const throw() { return "illegal_state"; }
@@ -414,7 +414,7 @@ const char* illegal_state::name() const throw() { return "illegal_state"; }
folder_not_found::~folder_not_found() throw() {}
folder_not_found::folder_not_found(const exception& other)
- : messaging_exception("Folder not found.", other) {}
+ : net_exception("Folder not found.", other) {}
exception* folder_not_found::clone() const { return new folder_not_found(*this); }
const char* folder_not_found::name() const throw() { return "folder_not_found"; }
@@ -426,7 +426,7 @@ const char* folder_not_found::name() const throw() { return "folder_not_found";
message_not_found::~message_not_found() throw() {}
message_not_found::message_not_found(const exception& other)
- : messaging_exception("Message not found.", other) {}
+ : net_exception("Message not found.", other) {}
exception* message_not_found::clone() const { return new message_not_found(*this); }
const char* message_not_found::name() const throw() { return "message_not_found"; }
@@ -438,7 +438,7 @@ const char* message_not_found::name() const throw() { return "message_not_found"
operation_not_supported::~operation_not_supported() throw() {}
operation_not_supported::operation_not_supported(const exception& other)
- : messaging_exception("Operation not supported.", other) {}
+ : net_exception("Operation not supported.", other) {}
exception* operation_not_supported::clone() const { return new operation_not_supported(*this); }
const char* operation_not_supported::name() const throw() { return "operation_not_supported"; }
@@ -450,7 +450,7 @@ const char* operation_not_supported::name() const throw() { return "operation_no
operation_timed_out::~operation_timed_out() throw() {}
operation_timed_out::operation_timed_out(const exception& other)
- : messaging_exception("Operation timed out.", other) {}
+ : net_exception("Operation timed out.", other) {}
exception* operation_timed_out::clone() const { return new operation_timed_out(*this); }
const char* operation_timed_out::name() const throw() { return "operation_timed_out"; }
@@ -462,7 +462,7 @@ const char* operation_timed_out::name() const throw() { return "operation_timed_
operation_cancelled::~operation_cancelled() throw() {}
operation_cancelled::operation_cancelled(const exception& other)
- : messaging_exception("Operation cancelled by the user.", other) {}
+ : net_exception("Operation cancelled by the user.", other) {}
exception* operation_cancelled::clone() const { return new operation_cancelled(*this); }
const char* operation_cancelled::name() const throw() { return "operation_cancelled"; }
@@ -474,7 +474,7 @@ const char* operation_cancelled::name() const throw() { return "operation_cancel
unfetched_object::~unfetched_object() throw() {}
unfetched_object::unfetched_object(const exception& other)
- : messaging_exception("Object not fetched.", other) {}
+ : net_exception("Object not fetched.", other) {}
exception* unfetched_object::clone() const { return new unfetched_object(*this); }
const char* unfetched_object::name() const throw() { return "unfetched_object"; }
@@ -486,7 +486,7 @@ const char* unfetched_object::name() const throw() { return "unfetched_object";
not_connected::~not_connected() throw() {}
not_connected::not_connected(const exception& other)
- : messaging_exception("Not connected to a service.", other) {}
+ : net_exception("Not connected to a service.", other) {}
exception* not_connected::clone() const { return new not_connected(*this); }
const char* not_connected::name() const throw() { return "not_connected"; }
@@ -498,7 +498,7 @@ const char* not_connected::name() const throw() { return "not_connected"; }
already_connected::~already_connected() throw() {}
already_connected::already_connected(const exception& other)
- : messaging_exception("Already connected to a service. Disconnect and retry.", other) {}
+ : net_exception("Already connected to a service. Disconnect and retry.", other) {}
exception* already_connected::clone() const { return new already_connected(*this); }
const char* already_connected::name() const throw() { return "already_connected"; }
@@ -510,7 +510,7 @@ const char* already_connected::name() const throw() { return "already_connected"
illegal_operation::~illegal_operation() throw() {}
illegal_operation::illegal_operation(const string& msg, const exception& other)
- : messaging_exception(msg.empty()
+ : net_exception(msg.empty()
? "Illegal operation."
: "Illegal operation: " + msg + ".",
other
@@ -527,7 +527,7 @@ const char* illegal_operation::name() const throw() { return "illegal_operation"
command_error::~command_error() throw() {}
command_error::command_error(const string& command, const string& response,
const string& desc, const exception& other)
- : messaging_exception(desc.empty()
+ : net_exception(desc.empty()
? "Error while executing command '" + command + "'."
: "Error while executing command '" + command + "': " + desc + ".",
other
@@ -548,7 +548,7 @@ const char* command_error::name() const throw() { return "command_error"; }
invalid_response::~invalid_response() throw() {}
invalid_response::invalid_response(const string& command, const string& response, const exception& other)
- : messaging_exception(command.empty()
+ : net_exception(command.empty()
? "Received invalid response."
: "Received invalid response for command '" + command + "'.",
other
@@ -569,7 +569,7 @@ const char* invalid_response::name() const throw() { return "invalid_response";
partial_fetch_not_supported::~partial_fetch_not_supported() throw() {}
partial_fetch_not_supported::partial_fetch_not_supported(const exception& other)
- : messaging_exception("Partial fetch not supported.", other) {}
+ : net_exception("Partial fetch not supported.", other) {}
exception* partial_fetch_not_supported::clone() const { return new partial_fetch_not_supported(*this); }
const char* partial_fetch_not_supported::name() const throw() { return "partial_fetch_not_supported"; }
@@ -581,7 +581,7 @@ const char* partial_fetch_not_supported::name() const throw() { return "partial_
malformed_url::~malformed_url() throw() {}
malformed_url::malformed_url(const string& error, const exception& other)
- : messaging_exception("Malformed URL: " + error + ".", other) {}
+ : net_exception("Malformed URL: " + error + ".", other) {}
exception* malformed_url::clone() const { return new malformed_url(*this); }
const char* malformed_url::name() const throw() { return "malformed_url"; }
@@ -593,7 +593,7 @@ const char* malformed_url::name() const throw() { return "malformed_url"; }
invalid_folder_name::~invalid_folder_name() throw() {}
invalid_folder_name::invalid_folder_name(const string& error, const exception& other)
- : messaging_exception(error.empty()
+ : net_exception(error.empty()
? "Invalid folder name: " + error + "."
: "Invalid folder name.",
other) {}
diff --git a/src/net/authHelper.cpp b/src/net/authHelper.cpp
new file mode 100644
index 00000000..e10b47ee
--- /dev/null
+++ b/src/net/authHelper.cpp
@@ -0,0 +1,105 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/authHelper.hpp"
+
+#include "vmime/config.hpp"
+#include "vmime/utility/md5.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+//
+// This code is based on the Sample Code published in the Appendix of
+// the RFC-2104: "HMAC: Keyed-Hashing for Message Authentication".
+//
+
+void hmac_md5(const string& text, const string& key, string& hexDigest)
+{
+ vmime_uint8 digest[16];
+
+ unsigned char ipad[65]; // inner padding - key XORd with ipad
+ unsigned char opad[65]; // outer padding - key XORd with opad
+
+ unsigned char tkey[16];
+ int tkeyLen;
+
+ // If key is longer than 64 bytes reset it to key = MD5(key)
+ if (key.length() > 64)
+ {
+ utility::md5 keyMD5;
+ keyMD5.update(reinterpret_cast <const vmime_uint8*>(key.data()), key.length());
+
+ std::copy(keyMD5.hash(), keyMD5.hash() + 16, tkey);
+ tkeyLen = 16;
+ }
+ else
+ {
+ std::copy(key.begin(), key.end(), tkey);
+ tkeyLen = key.length();
+ }
+
+ //
+ // the HMAC_MD5 transform looks like:
+ //
+ // MD5(K XOR opad, MD5(K XOR ipad, text))
+ //
+ // where K is an n byte key
+ // ipad is the byte 0x36 repeated 64 times
+ //
+ // opad is the byte 0x5c repeated 64 times
+ // and text is the data being protected
+ //
+
+ // Start out by storing key in pads
+ std::fill(ipad, ipad + sizeof(ipad), 0);
+ std::fill(opad, opad + sizeof(opad), 0);
+
+ std::copy(tkey, tkey + tkeyLen, ipad);
+ std::copy(tkey, tkey + tkeyLen, opad);
+
+ // XOR key with ipad and opad values
+ for (int i = 0 ; i < 64 ; ++i)
+ {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ // Perform inner MD5
+ utility::md5 innerMD5;
+ innerMD5.update(ipad, 64);
+ innerMD5.update(text);
+
+ std::copy(innerMD5.hash(), innerMD5.hash() + 16, digest);
+
+ // Perform outer MD5
+ utility::md5 outerMD5;
+ outerMD5.update(opad, 64);
+ outerMD5.update(digest, 16);
+
+ //std::copy(outerMD5.hash(), outerMD5.hash() + 16, digest);
+
+ hexDigest = outerMD5.hex();
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/authenticationInfos.cpp b/src/net/authenticationInfos.cpp
new file mode 100644
index 00000000..e5a93fb2
--- /dev/null
+++ b/src/net/authenticationInfos.cpp
@@ -0,0 +1,52 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/authenticationInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+authenticationInfos::authenticationInfos(const string& username, const string& password)
+ : m_username(username), m_password(password)
+{
+}
+
+
+authenticationInfos::authenticationInfos(const authenticationInfos& infos)
+ : object(), m_username(infos.m_username), m_password(infos.m_password)
+{
+}
+
+
+const string& authenticationInfos::getUsername() const
+{
+ return (m_username);
+}
+
+
+const string& authenticationInfos::getPassword() const
+{
+ return (m_password);
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/authenticator.cpp b/src/net/authenticator.cpp
new file mode 100644
index 00000000..d894915c
--- /dev/null
+++ b/src/net/authenticator.cpp
@@ -0,0 +1,33 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/authenticator.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+authenticator::~authenticator()
+{
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/builtinServices.inl b/src/net/builtinServices.inl
new file mode 100644
index 00000000..498d41eb
--- /dev/null
+++ b/src/net/builtinServices.inl
@@ -0,0 +1,56 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_BUILDING_DOC
+
+
+#define REGISTER_SERVICE(p_class, p_name) \
+ vmime::net::service::initializer <vmime::net::p_class> p_name(#p_name)
+
+
+#if VMIME_BUILTIN_MESSAGING_PROTO_POP3
+ #include "vmime/net/pop3/POP3Store.hpp"
+ REGISTER_SERVICE(pop3::POP3Store, pop3);
+#endif
+
+
+#if VMIME_BUILTIN_MESSAGING_PROTO_SMTP
+ #include "vmime/net/smtp/SMTPTransport.hpp"
+ REGISTER_SERVICE(smtp::SMTPTransport, smtp);
+#endif
+
+
+#if VMIME_BUILTIN_MESSAGING_PROTO_IMAP
+ #include "vmime/net/imap/IMAPStore.hpp"
+ REGISTER_SERVICE(imap::IMAPStore, imap);
+#endif
+
+
+#if VMIME_BUILTIN_MESSAGING_PROTO_MAILDIR
+ #include "vmime/net/maildir/maildirStore.hpp"
+ REGISTER_SERVICE(maildir::maildirStore, maildir);
+#endif
+
+#if VMIME_BUILTIN_MESSAGING_PROTO_SENDMAIL
+ #include "vmime/net/sendmail/sendmailTransport.hpp"
+ REGISTER_SERVICE(sendmail::sendmailTransport, sendmail);
+#endif
+
+
+#endif // VMIME_BUILDING_DOC
diff --git a/src/net/defaultAuthenticator.cpp b/src/net/defaultAuthenticator.cpp
new file mode 100644
index 00000000..59835b64
--- /dev/null
+++ b/src/net/defaultAuthenticator.cpp
@@ -0,0 +1,43 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/defaultAuthenticator.hpp"
+#include "vmime/net/session.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+defaultAuthenticator::defaultAuthenticator(weak_ref <session> sess, const string& prefix)
+ : m_session(sess), m_prefix(prefix)
+{
+}
+
+
+const authenticationInfos defaultAuthenticator::requestAuthInfos() const
+{
+ return (authenticationInfos
+ (m_session->getProperties()[m_prefix + "auth.username"],
+ m_session->getProperties()[m_prefix + "auth.password"]));
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/events.cpp b/src/net/events.cpp
new file mode 100644
index 00000000..d1818f03
--- /dev/null
+++ b/src/net/events.cpp
@@ -0,0 +1,111 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/events.hpp"
+#include "vmime/net/folder.hpp"
+
+#include <algorithm>
+
+
+namespace vmime {
+namespace net {
+namespace events {
+
+
+//
+// messageCountEvent
+//
+
+messageCountEvent::messageCountEvent
+ (ref <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());
+}
+
+
+ref <const folder> messageCountEvent::getFolder() const { return (m_folder); }
+const messageCountEvent::Types messageCountEvent::getType() const { return (m_type); }
+const std::vector <int>& messageCountEvent::getNumbers() const { return (m_nums); }
+
+
+void messageCountEvent::dispatch(messageCountListener* listener) const
+{
+ if (m_type == TYPE_ADDED)
+ listener->messagesAdded(*this);
+ else
+ listener->messagesRemoved(*this);
+}
+
+
+//
+// messageChangedEvent
+//
+
+messageChangedEvent::messageChangedEvent
+ (ref <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());
+}
+
+
+ref <const folder> messageChangedEvent::getFolder() const { return (m_folder); }
+const messageChangedEvent::Types messageChangedEvent::getType() const { return (m_type); }
+const std::vector <int>& messageChangedEvent::getNumbers() const { return (m_nums); }
+
+
+void messageChangedEvent::dispatch(messageChangedListener* listener) const
+{
+ listener->messageChanged(*this);
+}
+
+
+//
+// folderEvent
+//
+
+folderEvent::folderEvent
+ (ref <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)
+{
+}
+
+
+ref <const folder> folderEvent::getFolder() const { return (m_folder); }
+const folderEvent::Types folderEvent::getType() const { return (m_type); }
+
+
+void folderEvent::dispatch(folderListener* listener) const
+{
+ switch (m_type)
+ {
+ case TYPE_CREATED: listener->folderCreated(*this); break;
+ case TYPE_RENAMED: listener->folderRenamed(*this); break;
+ case TYPE_DELETED: listener->folderDeleted(*this); break;
+ }
+}
+
+
+} // events
+} // net
+} // vmime
diff --git a/src/net/folder.cpp b/src/net/folder.cpp
new file mode 100644
index 00000000..5172669b
--- /dev/null
+++ b/src/net/folder.cpp
@@ -0,0 +1,96 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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(const events::messageChangedEvent& event)
+{
+ for (std::list <events::messageChangedListener*>::iterator
+ it = m_messageChangedListeners.begin() ; it != m_messageChangedListeners.end() ; ++it)
+ {
+ event.dispatch(*it);
+ }
+}
+
+
+void folder::addMessageCountListener(events::messageCountListener* l)
+{
+ m_messageCountListeners.push_back(l);
+}
+
+
+void folder::removeMessageCountListener(events::messageCountListener* l)
+{
+ std::remove(m_messageCountListeners.begin(), m_messageCountListeners.end(), l);
+}
+
+
+void folder::notifyMessageCount(const events::messageCountEvent& event)
+{
+ for (std::list <events::messageCountListener*>::iterator
+ it = m_messageCountListeners.begin() ; it != m_messageCountListeners.end() ; ++it)
+ {
+ event.dispatch(*it);
+ }
+}
+
+
+void folder::addFolderListener(events::folderListener* l)
+{
+ m_folderListeners.push_back(l);
+}
+
+
+void folder::removeFolderListener(events::folderListener* l)
+{
+ std::remove(m_folderListeners.begin(), m_folderListeners.end(), l);
+}
+
+
+void folder::notifyFolder(const events::folderEvent& event)
+{
+ for (std::list <events::folderListener*>::iterator
+ it = m_folderListeners.begin() ; it != m_folderListeners.end() ; ++it)
+ {
+ event.dispatch(*it);
+ }
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp
new file mode 100644
index 00000000..5191dc94
--- /dev/null
+++ b/src/net/imap/IMAPConnection.cpp
@@ -0,0 +1,313 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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/platformDependant.hpp"
+
+#include <sstream>
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (m_store->getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (m_store->getInfos().hasProperty(getSession(), \
+ dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <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)
+{
+}
+
+
+IMAPConnection::~IMAPConnection()
+{
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+}
+
+
+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);
+
+ // Create the time-out handler
+ if (HAS_PROPERTY(PROPERTY_TIMEOUT_FACTORY))
+ {
+ timeoutHandlerFactory* tof = platformDependant::getHandler()->
+ getTimeoutHandlerFactory(GET_PROPERTY(string, PROPERTY_TIMEOUT_FACTORY));
+
+ m_timeoutHandler = tof->create();
+ }
+
+ // Create and connect the socket
+ socketFactory* sf = platformDependant::getHandler()->
+ getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
+
+ m_socket = sf->create();
+ m_socket->connect(address, port);
+
+
+ m_tag = vmime::create <IMAPTag>();
+ m_parser = vmime::create <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
+
+ utility::auto_ptr <IMAPParser::greeting> greet(m_parser->readGreeting());
+
+ if (greet->resp_cond_bye())
+ {
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(m_parser->lastLine());
+ }
+ else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH)
+ {
+ const authenticationInfos auth = m_auth->requestAuthInfos();
+
+ // TODO: other authentication methods
+
+ send(true, "LOGIN " + IMAPUtils::quoteString(auth.getUsername())
+ + " " + IMAPUtils::quoteString(auth.getPassword()), true);
+
+ utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ if (resp->isBad())
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LOGIN", m_parser->lastLine());
+ }
+ else if (resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(m_parser->lastLine());
+ }
+ }
+
+ // Get the hierarchy separator character
+ initHierarchySeparator();
+
+ // Switch to state "Authenticated"
+ setState(STATE_AUTHENTICATED);
+}
+
+
+const bool IMAPConnection::isConnected() const
+{
+ return (m_socket && m_socket->isConnected() &&
+ (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED));
+}
+
+
+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;
+}
+
+
+void IMAPConnection::initHierarchySeparator()
+{
+ send(true, "LIST \"\" \"\"", true);
+
+ vmime::utility::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", m_parser->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ if (respDataList.size() < 1 || respDataList[0]->response_data() == NULL)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "unexpected response");
+ }
+
+ const IMAPParser::mailbox_data* mailboxData =
+ static_cast <const IMAPParser::response_data*>(respDataList[0]->response_data())->
+ mailbox_data();
+
+ if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "invalid type");
+ }
+
+ if (mailboxData->mailbox_list()->quoted_char() == '\0')
+ {
+ internalDisconnect();
+ throw exceptions::command_error("LIST", m_parser->lastLine(), "no hierarchy separator");
+ }
+
+ m_hierarchySeparator = mailboxData->mailbox_list()->quoted_char();
+}
+
+
+void IMAPConnection::send(bool tag, const string& what, bool end)
+{
+#if VMIME_DEBUG
+ std::ostringstream oss;
+
+ if (tag)
+ {
+ ++(*m_tag);
+
+ oss << string(*m_tag);
+ oss << " ";
+ }
+
+ oss << what;
+
+ if (end)
+ oss << "\r\n";
+
+ m_socket->send(oss.str());
+#else
+ if (tag)
+ {
+ ++(*m_tag);
+
+ m_socket->send(*m_tag);
+ m_socket->send(" ");
+ }
+
+ m_socket->send(what);
+
+ if (end)
+ {
+ m_socket->send("\r\n");
+ }
+#endif
+}
+
+
+void IMAPConnection::sendRaw(const char* buffer, const int count)
+{
+ m_socket->sendRaw(buffer, count);
+}
+
+
+IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh)
+{
+ return (m_parser->readResponse(lh));
+}
+
+
+const IMAPConnection::ProtocolStates IMAPConnection::state() const
+{
+ return (m_state);
+}
+
+
+void IMAPConnection::setState(const ProtocolStates state)
+{
+ m_state = state;
+}
+
+const char IMAPConnection::hierarchySeparator() const
+{
+ return (m_hierarchySeparator);
+}
+
+
+ref <const IMAPTag> IMAPConnection::getTag() const
+{
+ return (m_tag);
+}
+
+
+ref <const IMAPParser> IMAPConnection::getParser() const
+{
+ return (m_parser);
+}
+
+
+weak_ref <const IMAPStore> IMAPConnection::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <IMAPStore> IMAPConnection::getStore()
+{
+ return (m_store);
+}
+
+
+ref <session> IMAPConnection::getSession()
+{
+ return (m_store->getSession());
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp
new file mode 100644
index 00000000..ea0ddfd9
--- /dev/null
+++ b/src/net/imap/IMAPFolder.cpp
@@ -0,0 +1,1608 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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/message.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/utility/smartPtr.hpp"
+
+#include <algorithm>
+#include <sstream>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPFolder::IMAPFolder(const folder::path& path, IMAPStore* store, const int type, const int flags)
+ : m_store(store), m_connection(m_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), m_messageCount(0), m_uidValidity(0)
+{
+ m_store->registerFolder(this);
+}
+
+
+IMAPFolder::~IMAPFolder()
+{
+ if (m_store)
+ {
+ if (m_open)
+ close(false);
+
+ m_store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ m_connection = NULL;
+ onClose();
+ }
+}
+
+
+const int IMAPFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+const 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);
+ }
+}
+
+
+const 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)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ // Open a connection for this folder
+ ref <IMAPConnection> connection =
+ vmime::create <IMAPConnection>(m_store, m_store->oneTimeAuthenticator());
+
+ 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()));
+
+ connection->send(true, oss.str(), true);
+
+ // Read the response
+ utility::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",
+ connection->getParser()->lastLine(), "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",
+ connection->getParser()->lastLine(), "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::UIDVALIDITY:
+
+ m_uidValidity = code->nz_number()->value();
+ 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;
+ }
+ case IMAPParser::mailbox_data::EXISTS:
+ {
+ m_messageCount = responseData->mailbox_data()->number()->value();
+ break;
+ }
+ case IMAPParser::mailbox_data::RECENT:
+ {
+ // TODO
+ break;
+ }
+
+ }
+ }
+ }
+
+ // 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)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ ref <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->connection();
+
+ m_open = false;
+ m_mode = -1;
+
+ m_uidValidity = 0;
+
+ 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)
+{
+ if (!m_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 (!m_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);
+
+
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder created
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_CREATED, m_path, m_path);
+
+ notifyFolder(event);
+}
+
+
+const bool IMAPFolder::exists()
+{
+ if (!isOpen() && !m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return (testExistAndGetType() != TYPE_UNDEFINED);
+}
+
+
+const 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);
+
+
+ utility::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",
+ m_connection->getParser()->lastLine(), "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",
+ m_connection->getParser()->lastLine(), "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);
+}
+
+
+const bool IMAPFolder::isOpen() const
+{
+ return (m_open);
+}
+
+
+ref <message> IMAPFolder::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 vmime::create <IMAPMessage>(this, num);
+}
+
+
+std::vector <ref <message> > IMAPFolder::getMessages(const int from, const int to)
+{
+ const int messageCount = getMessageCount();
+ const int to2 = (to == -1 ? messageCount : to);
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (to2 < from || from < 1 || to2 < 1 || from > messageCount || to2 > messageCount)
+ throw exceptions::message_not_found();
+
+ std::vector <ref <message> > v;
+
+ for (int i = from ; i <= to2 ; ++i)
+ v.push_back(vmime::create <IMAPMessage>(this, i));
+
+ return (v);
+}
+
+
+std::vector <ref <message> > IMAPFolder::getMessages(const std::vector <int>& nums)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <ref <message> > v;
+
+ for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ v.push_back(vmime::create <IMAPMessage>(this, *it));
+
+ return (v);
+}
+
+
+const int IMAPFolder::getMessageCount()
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_messageCount);
+}
+
+
+ref <folder> IMAPFolder::getFolder(const folder::path::component& name)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return vmime::create <IMAPFolder>(m_path / name, m_store);
+}
+
+
+std::vector <ref <folder> > IMAPFolder::getFolders(const bool recursive)
+{
+ if (!isOpen() && !m_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);
+
+
+ utility::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", m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+
+ std::vector <ref <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",
+ m_connection->getParser()->lastLine(), "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(vmime::create <IMAPFolder>(path, m_store,
+ IMAPUtils::folderTypeFromFlags(mailbox_flag_list),
+ IMAPUtils::folderFlagsFromFlags(mailbox_flag_list)));
+ }
+ }
+
+ return (v);
+}
+
+
+void IMAPFolder::fetchMessages(std::vector <ref <message> >& msg, const int options,
+ utility::progressionListener* progress)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ const int total = msg.size();
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ for (std::vector <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ (*it).dynamicCast <IMAPMessage>()->fetch(this, options);
+
+ if (progress)
+ progress->progress(++current, total);
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+void IMAPFolder::fetchMessage(ref <message> msg, const int options)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ msg.dynamicCast <IMAPMessage>()->fetch(this, options);
+}
+
+
+const int IMAPFolder::getFetchCapabilities() const
+{
+ return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_STRUCTURE |
+ FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID |
+ FETCH_IMPORTANCE);
+}
+
+
+ref <folder> IMAPFolder::getParent()
+{
+ if (m_path.isEmpty())
+ return NULL;
+ else
+ return vmime::create <IMAPFolder>(m_path.getParent(), m_store);
+}
+
+
+weak_ref <const store> IMAPFolder::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <store> IMAPFolder::getStore()
+{
+ return (m_store);
+}
+
+
+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 = NULL;
+}
+
+
+void IMAPFolder::deleteMessage(const int num)
+{
+ if (!m_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 << "STORE " << num << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() == num &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::deleteMessages(const int from, const int to)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_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 << "STORE " << from << ":";
+
+ if (to == -1) command << m_messageCount;
+ else command << to;
+
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::deleteMessages(const std::vector <int>& nums)
+{
+ if (nums.empty())
+ throw exceptions::invalid_argument();
+
+ if (!m_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");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE ";
+ command << IMAPUtils::listToSet(list, m_messageCount, true);
+ command << " +FLAGS.SILENT (\\Deleted)";
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update local flags
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()))
+ {
+ if ((*it)->m_flags != message::FLAG_UNDEFINED)
+ (*it)->m_flags |= message::FLAG_DELETED;
+ }
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, list);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_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");
+
+ std::ostringstream oss;
+
+ if (to == -1)
+ oss << from << ":*";
+ else
+ oss << from << ":" << to;
+
+ setMessageFlags(oss.str(), flags, mode);
+
+ // Update local flags
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= flags;
+ }
+ }
+
+ break;
+ }
+ case message::FLAG_MODE_REMOVE:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags &= ~flags;
+ }
+ }
+
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const std::vector <int>& nums, const int flags, const int mode)
+{
+ if (!m_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");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Delegates call
+ setMessageFlags(IMAPUtils::listToSet(list, m_messageCount, true), flags, mode);
+
+ // Update local flags
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags |= flags;
+ }
+ }
+
+ break;
+ }
+ case message::FLAG_MODE_REMOVE:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags &= ~flags;
+ }
+ }
+
+ break;
+ }
+ default:
+ case message::FLAG_MODE_SET:
+ {
+ for (std::vector <IMAPMessage*>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ if (std::binary_search(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void IMAPFolder::setMessageFlags(const string& set, const int flags, const int mode)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << set;
+
+ switch (mode)
+ {
+ case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break;
+ case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break;
+ default:
+ case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; 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
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+ }
+}
+
+
+void IMAPFolder::addMessage(ref <vmime::message> msg, const int flags,
+ vmime::datetime* date, utility::progressionListener* progress)
+{
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ msg->generate(ossAdapter);
+
+ const std::string& str = oss.str();
+ utility::inputStreamStringAdapter strAdapter(str);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags,
+ vmime::datetime* date, utility::progressionListener* progress)
+{
+ if (!m_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 << "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
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Send message data
+ const int total = size;
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ char buffer[65536];
+
+ while (!is.eof())
+ {
+ // Read some data from the input stream
+ const int read = is.read(buffer, sizeof(buffer));
+ current += read;
+
+ // Put read data into socket output stream
+ m_connection->sendRaw(buffer, read);
+
+ // Notify progression
+ if (progress)
+ progress->progress(current, total);
+ }
+
+ m_connection->send(false, "", true);
+
+ if (progress)
+ progress->stop(total);
+
+ // Get the response
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify message added
+ std::vector <int> nums;
+ nums.push_back(m_messageCount + 1);
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ m_messageCount++;
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::expunge()
+{
+ if (!m_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
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update the numbering of the messages
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ std::vector <int> nums;
+
+ 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("EXPUNGE",
+ m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::message_data* messageData =
+ (*it)->response_data()->message_data();
+
+ // We are only interested in responses of type "EXPUNGE"
+ if (messageData == NULL ||
+ messageData->type() != IMAPParser::message_data::EXPUNGE)
+ {
+ continue;
+ }
+
+ const int number = messageData->number();
+
+ nums.push_back(number);
+
+ for (std::vector <IMAPMessage*>::iterator jt =
+ m_messages.begin() ; jt != m_messages.end() ; ++jt)
+ {
+ if ((*jt)->m_num == number)
+ (*jt)->m_expunged = true;
+ else if ((*jt)->m_num > number)
+ (*jt)->m_num--;
+ }
+ }
+
+ m_messageCount -= nums.size();
+
+ // Notify message expunged
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = m_messageCount;
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::rename(const folder::path& newPath)
+{
+ if (!m_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 (!m_store->isValidFolderName(newPath.getLastComponent()))
+ throw exceptions::invalid_folder_name();
+
+ // Build the request text
+ std::ostringstream command;
+ 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
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Notify folder renamed
+ folder::path oldPath(m_path);
+
+ m_path = newPath;
+ m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, newPath);
+
+ notifyFolder(event);
+
+ // Notify folders with the same path and sub-folders
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == oldPath)
+ {
+ (*it)->m_path = newPath;
+ (*it)->m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ 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);
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path);
+
+ (*it)->notifyFolder(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessage(const folder::path& dest, const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Construct set
+ std::ostringstream set;
+ set << num;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount++;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, const int from, const int to)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ // Construct set
+ std::ostringstream set;
+
+ if (to == -1)
+ set << from << ":*";
+ else
+ set << from << ":" << to;
+
+ // Delegate message copy
+ copyMessages(set.str(), dest);
+
+ // Notify message count changed
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Delegate message copy
+ copyMessages(IMAPUtils::listToSet(nums, m_messageCount), dest);
+
+ // Notify message count changed
+ const int count = nums.size();
+
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == dest)
+ {
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->m_messageCount += count;
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void IMAPFolder::copyMessages(const string& set, const folder::path& dest)
+{
+ // Build the request text
+ std::ostringstream command;
+ command << "COPY " << set << " ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), dest));
+
+ // Send the request
+ m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::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",
+ m_connection->getParser()->lastLine(), "bad response");
+ }
+}
+
+
+void IMAPFolder::status(int& count, int& unseen)
+{
+ count = 0;
+ unseen = 0;
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STATUS ";
+ command << IMAPUtils::quoteString(IMAPUtils::pathToString
+ (m_connection->hierarchySeparator(), getFullPath()));
+ command << " (MESSAGES UNSEEN)";
+
+ // Send the request
+ m_store->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_store->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",
+ m_store->m_connection->getParser()->lastLine(), "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("STATUS",
+ m_store->m_connection->getParser()->lastLine(), "invalid response");
+ }
+
+ const IMAPParser::response_data* responseData = (*it)->response_data();
+
+ if (responseData->mailbox_data() &&
+ responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS)
+ {
+ const std::vector <IMAPParser::status_info*>& statusList =
+ responseData->mailbox_data()->status_info_list();
+
+ for (std::vector <IMAPParser::status_info*>::const_iterator
+ jt = statusList.begin() ; jt != statusList.end() ; ++jt)
+ {
+ switch ((*jt)->status_att()->type())
+ {
+ case IMAPParser::status_att::MESSAGES:
+
+ count = (*jt)->number()->value();
+ break;
+
+ case IMAPParser::status_att::UNSEEN:
+
+ unseen = (*jt)->number()->value();
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Notify message count changed (new messages)
+ 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;
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = count;
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+ }
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp
new file mode 100644
index 00000000..0d9e5e00
--- /dev/null
+++ b/src/net/imap/IMAPMessage.cpp
@@ -0,0 +1,859 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/imap/IMAPParser.hpp"
+#include "vmime/net/imap/IMAPMessage.hpp"
+#include "vmime/net/imap/IMAPFolder.hpp"
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+#include "vmime/net/imap/IMAPUtils.hpp"
+
+#include <sstream>
+#include <iterator>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+//
+// IMAPpart
+//
+
+class IMAPstructure;
+
+class IMAPpart : public part
+{
+private:
+
+ friend class vmime::creator;
+
+ IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_mpart* mpart);
+ IMAPpart(weak_ref <IMAPpart> parent, const int number, const IMAPParser::body_type_1part* part);
+
+public:
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ weak_ref <const IMAPpart> getParent() const { return (m_parent); }
+
+ const mediaType& getType() const { return (m_mediaType); }
+ const int getSize() const { return (m_size); }
+ const int getNumber() const { return (m_number); }
+
+ const header& getHeader() const
+ {
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+ else
+ return (*m_header);
+ }
+
+
+ static ref <IMAPpart> create
+ (weak_ref <IMAPpart> parent, const int number, const IMAPParser::body* body)
+ {
+ if (body->body_type_mpart())
+ return vmime::create <IMAPpart>(parent, number, body->body_type_mpart());
+ else
+ return vmime::create <IMAPpart>(parent, number, body->body_type_1part());
+ }
+
+
+ header& getOrCreateHeader()
+ {
+ if (m_header != NULL)
+ return (*m_header);
+ else
+ return (*(m_header = vmime::create <header>()));
+ }
+
+private:
+
+ ref <IMAPstructure> m_structure;
+ weak_ref <IMAPpart> m_parent;
+ ref <header> m_header;
+
+ int m_number;
+ int m_size;
+ mediaType m_mediaType;
+};
+
+
+
+//
+// IMAPstructure
+//
+
+class IMAPstructure : public structure
+{
+private:
+
+ IMAPstructure()
+ {
+ }
+
+public:
+
+ IMAPstructure(const IMAPParser::body* body)
+ {
+ m_parts.push_back(IMAPpart::create(NULL, 1, body));
+ }
+
+ IMAPstructure(weak_ref <IMAPpart> parent, const std::vector <IMAPParser::body*>& list)
+ {
+ int number = 1;
+
+ for (std::vector <IMAPParser::body*>::const_iterator
+ it = list.begin() ; it != list.end() ; ++it, ++number)
+ {
+ m_parts.push_back(IMAPpart::create(parent, number, *it));
+ }
+ }
+
+
+ const part& operator[](const int x) const
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ part& operator[](const int x)
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ const int getCount() const
+ {
+ return (m_parts.size());
+ }
+
+
+ static IMAPstructure* emptyStructure()
+ {
+ return (&m_emptyStructure);
+ }
+
+private:
+
+ static IMAPstructure m_emptyStructure;
+
+ std::vector <ref <IMAPpart> > m_parts;
+};
+
+
+IMAPstructure IMAPstructure::m_emptyStructure;
+
+
+
+IMAPpart::IMAPpart(weak_ref <IMAPpart> 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());
+
+ m_structure = vmime::create <IMAPstructure>
+ (thisWeakRef().dynamicCast <IMAPpart>(), mpart->list());
+}
+
+
+IMAPpart::IMAPpart(weak_ref <IMAPpart> 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;
+}
+
+
+const class structure& IMAPpart::getStructure() const
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*IMAPstructure::emptyStructure());
+}
+
+
+class structure& IMAPpart::getStructure()
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*IMAPstructure::emptyStructure());
+}
+
+
+
+#ifndef VMIME_BUILDING_DOC
+
+//
+// IMAPMessage_literalHandler
+//
+
+class IMAPMessage_literalHandler : public IMAPParser::literalHandler
+{
+public:
+
+ IMAPMessage_literalHandler(utility::outputStream& os, utility::progressionListener* 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::progressionListener* m_progress;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPMessage
+//
+
+
+IMAPMessage::IMAPMessage(IMAPFolder* folder, const int num)
+ : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED),
+ m_expunged(false), m_structure(NULL)
+{
+ m_folder->registerMessage(this);
+}
+
+
+IMAPMessage::~IMAPMessage()
+{
+ if (m_folder)
+ m_folder->unregisterMessage(this);
+}
+
+
+void IMAPMessage::onFolderClosed()
+{
+ m_folder = NULL;
+}
+
+
+const int IMAPMessage::getNumber() const
+{
+ return (m_num);
+}
+
+
+const message::uid IMAPMessage::getUniqueId() const
+{
+ return (m_uid);
+}
+
+
+const int IMAPMessage::getSize() const
+{
+ if (m_size == -1)
+ throw exceptions::unfetched_object();
+
+ return (m_size);
+}
+
+
+const bool IMAPMessage::isExpunged() const
+{
+ return (m_expunged);
+}
+
+
+const int IMAPMessage::getFlags() const
+{
+ if (m_flags == FLAG_UNDEFINED)
+ throw exceptions::unfetched_object();
+
+ return (m_flags);
+}
+
+
+const structure& IMAPMessage::getStructure() const
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+structure& IMAPMessage::getStructure()
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+ref <const header> IMAPMessage::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+
+ return (m_header);
+}
+
+
+void IMAPMessage::extract(utility::outputStream& os, utility::progressionListener* progress,
+ const int start, const int length, const bool peek) const
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ extract(NULL, os, progress, start, length, false, peek);
+}
+
+
+void IMAPMessage::extractPart
+ (const part& p, utility::outputStream& os, utility::progressionListener* progress,
+ const int start, const int length, const bool peek) const
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ extract(&p, os, progress, start, length, false, peek);
+}
+
+
+void IMAPMessage::fetchPartHeader(part& p)
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ extract(&p, ossAdapter, NULL, 0, -1, true, true);
+
+ static_cast <IMAPpart&>(p).getOrCreateHeader().parse(oss.str());
+}
+
+
+void IMAPMessage::extract(const part* p, utility::outputStream& os,
+ utility::progressionListener* progress, const int start,
+ const int length, const bool headerOnly, const bool peek) const
+{
+ IMAPMessage_literalHandler literalHandler(os, progress);
+
+ // Construct section identifier
+ std::ostringstream section;
+
+ if (p != NULL)
+ {
+ weak_ref <const IMAPpart> currentPart = static_cast <const IMAPpart*>(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;
+ }
+ }
+
+ // Build the request text
+ std::ostringstream command;
+
+ command << "FETCH " << m_num << " BODY";
+ if (peek) command << ".PEEK";
+ command << "[";
+ command << section.str();
+ if (headerOnly) command << ".MIME"; // "MIME" not "HEADER" for parts
+ command << "]";
+
+ if (start != 0 || length != -1)
+ command << "<" << start << "." << length << ">";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp
+ (m_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",
+ m_folder->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+
+ if (!headerOnly)
+ {
+ // TODO: update the flags (eg. flag "\Seen" may have been set)
+ }
+}
+
+
+void IMAPMessage::fetch(IMAPFolder* folder, const int options)
+{
+ if (m_folder != folder)
+ throw exceptions::folder_not_found();
+
+ // TODO: optimization: send the request for multiple
+ // messages at the same time (FETCH x:y)
+
+ // 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 & folder::FETCH_SIZE)
+ items.push_back("RFC822.SIZE");
+
+ if (options & folder::FETCH_FLAGS)
+ items.push_back("FLAGS");
+
+ if (options & folder::FETCH_STRUCTURE)
+ items.push_back("BODYSTRUCTURE");
+
+ if (options & folder::FETCH_UID)
+ items.push_back("UID");
+
+ if (options & folder::FETCH_FULL_HEADER)
+ items.push_back("RFC822.HEADER");
+ else
+ {
+ if (options & folder::FETCH_ENVELOPE)
+ items.push_back("ENVELOPE");
+
+ std::vector <string> headerFields;
+
+ if (options & folder::FETCH_CONTENT_INFO)
+ headerFields.push_back("CONTENT_TYPE");
+
+ if (options & folder::FETCH_IMPORTANCE)
+ {
+ headerFields.push_back("IMPORTANCE");
+ headerFields.push_back("X-PRIORITY");
+ }
+
+ 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 << "FETCH " << m_num << " (";
+
+ for (std::vector <string>::const_iterator it = items.begin() ;
+ it != items.end() ; ++it)
+ {
+ if (it != items.begin()) command << " ";
+ command << *it;
+ }
+
+ command << ")";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_folder->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",
+ m_folder->m_connection->getParser()->lastLine(), "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("FETCH",
+ m_folder->m_connection->getParser()->lastLine(), "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;
+
+ if (static_cast <int>(messageData->number()) != m_num)
+ continue;
+
+ // Process fetch response for this message
+ processFetchResponse(options, messageData->msg_att());
+ }
+}
+
+
+void IMAPMessage::processFetchResponse
+ (const int options, const IMAPParser::msg_att* msgAtt)
+{
+ // Get message attributes
+ const std::vector <IMAPParser::msg_att_item*> atts =
+ msgAtt->items();
+
+ int flags = 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:
+ {
+ flags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list());
+ break;
+ }
+ case IMAPParser::msg_att_item::UID:
+ {
+ std::ostringstream oss;
+ oss << m_folder->m_uidValidity << ":" << (*it)->unique_id()->value();
+
+ m_uid = oss.str();
+ break;
+ }
+ case IMAPParser::msg_att_item::ENVELOPE:
+ {
+ if (!(options & folder::FETCH_FULL_HEADER))
+ {
+ const IMAPParser::envelope* env = (*it)->envelope();
+ ref <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;
+ convertAddressList(*(env->env_from()), from);
+
+ if (!from.isEmpty())
+ hdr->From()->setValue(*(from.getMailboxAt(0)));
+
+ // To
+ mailboxList to;
+ convertAddressList(*(env->env_to()), to);
+
+ hdr->To()->setValue(to);
+
+ // Sender
+ mailboxList sender;
+ convertAddressList(*(env->env_sender()), sender);
+
+ if (!sender.isEmpty())
+ hdr->Sender()->setValue(*(sender.getMailboxAt(0)));
+
+ // Reply-to
+ mailboxList replyTo;
+ convertAddressList(*(env->env_reply_to()), replyTo);
+
+ if (!replyTo.isEmpty())
+ hdr->ReplyTo()->setValue(*(replyTo.getMailboxAt(0)));
+
+ // Cc
+ mailboxList cc;
+ convertAddressList(*(env->env_cc()), cc);
+
+ if (!cc.isEmpty())
+ hdr->Cc()->setValue(cc);
+
+ // Bcc
+ mailboxList bcc;
+ convertAddressList(*(env->env_bcc()), bcc);
+
+ if (!bcc.isEmpty())
+ hdr->Bcc()->setValue(bcc);
+ }
+
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_STRUCTURE:
+ {
+ m_structure = vmime::create <IMAPstructure>((*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 = (*it)->number()->value();
+ break;
+ }
+ case IMAPParser::msg_att_item::BODY_SECTION:
+ {
+ if (!(options & folder::FETCH_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 <ref <headerField> > fields = tempHeader.getFieldList();
+
+ for (std::vector <ref <headerField> >::const_iterator jt = fields.begin() ;
+ jt != fields.end() ; ++jt)
+ {
+ hdr.appendField((*jt)->clone().dynamicCast <headerField>());
+ }
+ }
+ }
+
+ 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;
+ }
+
+ }
+ }
+
+ if (options & folder::FETCH_FLAGS)
+ m_flags = flags;
+}
+
+
+ref <header> IMAPMessage::getOrCreateHeader()
+{
+ if (m_header != NULL)
+ return (m_header);
+ else
+ return (m_header = vmime::create <header>());
+}
+
+
+void IMAPMessage::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(vmime::create <mailbox>(name, email));
+ }
+}
+
+
+void IMAPMessage::setFlags(const int flags, const int mode)
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+ else if (m_folder->m_mode == folder::MODE_READ_ONLY)
+ throw exceptions::illegal_state("Folder is read-only");
+
+ // Build the request text
+ std::ostringstream command;
+ command << "STORE " << m_num;
+
+ switch (mode)
+ {
+ case FLAG_MODE_ADD: command << " +FLAGS"; break;
+ case FLAG_MODE_REMOVE: command << " -FLAGS"; break;
+ default:
+ case FLAG_MODE_SET: command << " FLAGS"; break;
+ }
+
+ if (m_flags == FLAG_UNDEFINED) // Update local flags only if they
+ command << ".SILENT "; // have been fetched previously
+ else
+ command << " ";
+
+ std::vector <string> flagList;
+
+ if (flags & FLAG_REPLIED) flagList.push_back("\\Answered");
+ if (flags & FLAG_MARKED) flagList.push_back("\\Flagged");
+ if (flags & FLAG_DELETED) flagList.push_back("\\Deleted");
+ if (flags & FLAG_SEEN) flagList.push_back("\\Seen");
+
+ if (!flagList.empty())
+ {
+ command << "(";
+
+ if (flagList.size() >= 2)
+ {
+ std::copy(flagList.begin(), flagList.end() - 1,
+ std::ostream_iterator <string>(command, " "));
+ }
+
+ command << *(flagList.end() - 1) << ")";
+
+ // Send the request
+ m_folder->m_connection->send(true, command.str(), true);
+
+ // Get the response
+ utility::auto_ptr <IMAPParser::response> resp(m_folder->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",
+ m_folder->m_connection->getParser()->lastLine(), "bad response");
+ }
+
+ // Update the local flags for this message
+ if (m_flags != FLAG_UNDEFINED)
+ {
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ int newFlags = 0;
+
+ for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator
+ it = respDataList.begin() ; it != respDataList.end() ; ++it)
+ {
+ if ((*it)->response_data() == NULL)
+ continue;
+
+ 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 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::FLAGS)
+ newFlags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list());
+ }
+ }
+
+ m_flags = newFlags;
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(m_num);
+
+ events::messageChangedEvent event
+ (m_folder->thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ for (std::list <IMAPFolder*>::iterator it = m_folder->m_store->m_folders.begin() ;
+ it != m_folder->m_store->m_folders.end() ; ++it)
+ {
+ if ((*it)->getFullPath() == m_folder->m_path)
+ (*it)->notifyMessageChanged(event);
+ }
+ }
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp
new file mode 100644
index 00000000..c8dab178
--- /dev/null
+++ b/src/net/imap/IMAPStore.cpp
@@ -0,0 +1,308 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/imap/IMAPStore.hpp"
+#include "vmime/net/imap/IMAPFolder.hpp"
+#include "vmime/net/imap/IMAPConnection.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+
+#include <map>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+//
+// IMAPauthenticator: private class used internally
+//
+// Used to request user credentials only in the first authentication
+// and reuse this information the next times
+//
+
+class IMAPauthenticator : public authenticator
+{
+public:
+
+ IMAPauthenticator(ref <authenticator> auth)
+ : m_auth(auth), m_infos(NULL)
+ {
+ }
+
+ ~IMAPauthenticator()
+ {
+ }
+
+ const authenticationInfos requestAuthInfos() const
+ {
+ if (m_infos == NULL)
+ m_infos = vmime::create <authenticationInfos>(m_auth->requestAuthInfos());
+
+ return (*m_infos);
+ }
+
+private:
+
+ ref <authenticator> m_auth;
+ mutable ref <authenticationInfos> m_infos;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+//
+// IMAPStore
+//
+
+IMAPStore::IMAPStore(ref <session> sess, ref <authenticator> auth)
+ : store(sess, getInfosInstance(), auth),
+ m_connection(NULL), m_oneTimeAuth(NULL)
+{
+}
+
+
+IMAPStore::~IMAPStore()
+{
+ if (isConnected())
+ disconnect();
+}
+
+
+ref <authenticator> IMAPStore::oneTimeAuthenticator()
+{
+ return (m_oneTimeAuth);
+}
+
+
+const string IMAPStore::getProtocolName() const
+{
+ return "imap";
+}
+
+
+ref <folder> IMAPStore::getRootFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(folder::path(), this);
+}
+
+
+ref <folder> IMAPStore::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(folder::path::component("INBOX"), this);
+}
+
+
+ref <folder> IMAPStore::getFolder(const folder::path& path)
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <IMAPFolder>(path, this);
+}
+
+
+const bool IMAPStore::isValidFolderName(const folder::path::component& /* name */) const
+{
+ return true;
+}
+
+
+void IMAPStore::connect()
+{
+ if (isConnected())
+ throw exceptions::already_connected();
+
+ m_oneTimeAuth = vmime::create <IMAPauthenticator>(getAuthenticator());
+
+ m_connection = vmime::create <IMAPConnection>
+ (thisWeakRef().dynamicCast <IMAPStore>(), m_oneTimeAuth);
+
+ try
+ {
+ m_connection->connect();
+ }
+ catch (std::exception&)
+ {
+ m_connection = NULL;
+ throw;
+ }
+}
+
+
+const bool IMAPStore::isConnected() const
+{
+ return (m_connection && m_connection->isConnected());
+}
+
+
+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_oneTimeAuth = NULL;
+
+ m_connection = NULL;
+}
+
+
+void IMAPStore::noop()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ m_connection->send(true, "NOOP", true);
+
+ utility::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", m_connection->getParser()->lastLine());
+ }
+}
+
+
+ref <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);
+}
+
+
+const 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
+
+IMAPStore::_infos IMAPStore::sm_infos;
+
+
+const serviceInfos& IMAPStore::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& IMAPStore::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string IMAPStore::_infos::getPropertyPrefix() const
+{
+ return "store.imap.";
+}
+
+
+const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const
+{
+ static props p =
+ {
+ // IMAP-specific options
+ // (none)
+
+ // Common properties
+ property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "143"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // IMAP-specific options
+ // (none)
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return (list);
+}
+
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPTag.cpp b/src/net/imap/IMAPTag.cpp
new file mode 100644
index 00000000..5331d8bf
--- /dev/null
+++ b/src/net/imap/IMAPTag.cpp
@@ -0,0 +1,99 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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);
+}
+
+
+IMAPTag::IMAPTag(const IMAPTag& tag)
+ : object(), m_number(tag.m_number)
+{
+ m_tag.resize(4);
+}
+
+
+IMAPTag::IMAPTag()
+ : m_number(0)
+{
+ m_tag.resize(4);
+}
+
+
+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);
+}
+
+
+const 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] = '0' + (m_number % 1000) / 100;
+ m_tag[2] = '0' + (m_number % 100) / 10;
+ m_tag[3] = '0' + (m_number % 10);
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp
new file mode 100644
index 00000000..11111b13
--- /dev/null
+++ b/src/net/imap/IMAPUtils.cpp
@@ -0,0 +1,555 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/imap/IMAPUtils.hpp"
+#include "vmime/net/message.hpp"
+
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+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() ;
+ !needQuoting && 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 (int 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];
+
+ // 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;
+
+ // Replace hierarchy separator with an equivalent UTF-7 Base64 sequence
+ if (!inB64sequence && c == hierarchySeparator)
+ {
+ out += "&" + hsUTF7 + "-";
+ continue;
+ }
+
+ 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
+ case '/':
+ {
+ out += inB64sequence ? ',' : '/';
+ break;
+ }
+ // '&' (0x26) is represented by the two-octet sequence "&-"
+ case '&':
+ {
+ if (!inB64sequence)
+ out += "&-";
+ else
+ out += '&';
+
+ break;
+ }
+ default:
+ {
+ out += c;
+ break;
+ }
+
+ }
+ }
+
+ 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)));
+}
+
+
+const 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);
+}
+
+
+const 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);
+}
+
+
+const 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;
+
+ default:
+ //case IMAPParser::flag::UNKNOWN:
+ //case IMAPParser::flag::DRAFT:
+ 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 (!flagList.empty())
+ {
+ std::ostringstream res;
+ 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 "";
+}
+
+
+// This function builds a "IMAP set" given a list. Try to group consecutive
+// message numbers to reduce the list.
+//
+// Example:
+// IN = "1,2,3,4,5,7,8,13,15,16,17"
+// OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17)
+
+const string IMAPUtils::listToSet(const std::vector <int>& list, const int max,
+ const bool alreadySorted)
+{
+ // Sort a copy of the list (if not already sorted)
+ std::vector <int> temp;
+
+ if (!alreadySorted)
+ {
+ temp.resize(list.size());
+ std::copy(list.begin(), list.end(), temp.begin());
+
+ std::sort(temp.begin(), temp.end());
+ }
+
+ const std::vector <int>& theList = (alreadySorted ? list : temp);
+
+ // Build the set
+ std::ostringstream res;
+ int previous = -1, setBegin = -1;
+
+ for (std::vector <int>::const_iterator it = theList.begin() ;
+ it != theList.end() ; ++it)
+ {
+ const int current = *it;
+
+ if (previous == -1)
+ {
+ res << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ else
+ {
+ if (current == previous + 1)
+ {
+ previous = current;
+ }
+ else
+ {
+ if (setBegin != previous)
+ {
+ res << ":" << previous << "," << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ else
+ {
+ if (setBegin != current) // skip duplicates
+ res << "," << current;
+
+ previous = current;
+ setBegin = current;
+ }
+ }
+ }
+ }
+
+ if (previous != setBegin)
+ {
+ if (previous == max)
+ res << ":*";
+ else
+ res << ":" << previous;
+ }
+
+ return (res.str());
+}
+
+
+const string IMAPUtils::dateTime(const vmime::datetime& date)
+{
+ std::ostringstream res;
+
+ // 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());
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp
new file mode 100644
index 00000000..06d1ac12
--- /dev/null
+++ b/src/net/maildir/maildirFolder.cpp
@@ -0,0 +1,1387 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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/utility/smartPtr.hpp"
+
+#include "vmime/message.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+maildirFolder::maildirFolder(const folder::path& path, weak_ref <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)
+{
+ m_store->registerFolder(this);
+}
+
+
+maildirFolder::~maildirFolder()
+{
+ if (m_store)
+ {
+ if (m_open)
+ close(false);
+
+ m_store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ close(false);
+ }
+}
+
+
+void maildirFolder::onStoreDisconnected()
+{
+ m_store = NULL;
+}
+
+
+const int maildirFolder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+const int maildirFolder::getType()
+{
+ if (m_path.isEmpty())
+ return (TYPE_CONTAINS_FOLDERS);
+ else
+ return (TYPE_CONTAINS_FOLDERS | TYPE_CONTAINS_MESSAGES);
+}
+
+
+const int maildirFolder::getFlags()
+{
+ int flags = 0;
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ ref <utility::file> rootDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER));
+
+ ref <utility::fileIterator> it = rootDir->getFiles();
+
+ while (it->hasMoreElements())
+ {
+ ref <utility::file> file = it->nextElement();
+
+ if (maildirUtils::isSubfolderDirectory(*file))
+ {
+ flags |= FLAG_CHILDREN; // Contains at least one sub-folder
+ break;
+ }
+ }
+
+ 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 */)
+{
+ if (!m_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)
+{
+ if (!m_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 */)
+{
+ if (!m_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 (!m_store->isValidFolderName(m_name))
+ throw exceptions::invalid_folder_name();
+
+ // Create directory on file system
+ try
+ {
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ if (!fsf->isValidPath(maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT)))
+ throw exceptions::invalid_folder_name();
+
+ ref <utility::file> rootDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT));
+
+ ref <utility::file> newDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW));
+ ref <utility::file> tmpDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP));
+ ref <utility::file> curDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR));
+
+ rootDir->createDirectory(true);
+
+ newDir->createDirectory(false);
+ tmpDir->createDirectory(false);
+ curDir->createDirectory(false);
+ }
+ catch (exceptions::filesystem_exception& e)
+ {
+ throw exceptions::command_error("CREATE", "", "File system exception", e);
+ }
+
+ // Notify folder created
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_CREATED, m_path, m_path);
+
+ notifyFolder(event);
+}
+
+
+const bool maildirFolder::exists()
+{
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ ref <utility::file> rootDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT));
+
+ ref <utility::file> newDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW));
+ ref <utility::file> tmpDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP));
+ ref <utility::file> curDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR));
+
+ return (rootDir->exists() && rootDir->isDirectory() &&
+ newDir->exists() && newDir->isDirectory() &&
+ tmpDir->exists() && tmpDir->isDirectory() &&
+ curDir->exists() && curDir->isDirectory());
+}
+
+
+const bool maildirFolder::isOpen() const
+{
+ return (m_open);
+}
+
+
+void maildirFolder::scanFolder()
+{
+ try
+ {
+ m_messageCount = 0;
+ m_unreadMessageCount = 0;
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ utility::file::path newDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_NEW);
+ ref <utility::file> newDir = fsf->create(newDirPath);
+
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+ ref <utility::file> curDir = fsf->create(curDirPath);
+
+ // New received messages (new/)
+ ref <utility::fileIterator> nit = newDir->getFiles();
+ std::vector <utility::file::path::component> newMessageFilenames;
+
+ while (nit->hasMoreElements())
+ {
+ ref <utility::file> file = nit->nextElement();
+
+ if (maildirUtils::isMessageFile(*file))
+ newMessageFilenames.push_back(file->getFullPath().getLastComponent());
+ }
+
+ // Current messages (cur/)
+ ref <utility::fileIterator> cit = curDir->getFiles();
+ std::vector <utility::file::path::component> curMessageFilenames;
+
+ while (cit->hasMoreElements())
+ {
+ ref <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'
+ ref <utility::file> file = fsf->create(newDirPath / *it);
+ file->rename(curDirPath / newFilename);
+
+ // Append to message list
+ messageInfos msgInfos;
+ msgInfos.path = newFilename;
+ 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;
+ 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...
+ }
+}
+
+
+ref <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 vmime::create <maildirMessage>
+ (thisWeakRef().dynamicCast <maildirFolder>(), num);
+}
+
+
+std::vector <ref <message> > maildirFolder::getMessages(const int from, const int to)
+{
+ const int to2 = (to == -1 ? m_messageCount : to);
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (to2 < from || from < 1 || to2 < 1 || from > m_messageCount || to2 > m_messageCount)
+ throw exceptions::message_not_found();
+
+ std::vector <ref <message> > v;
+
+ for (int i = from ; i <= to2 ; ++i)
+ {
+ v.push_back(vmime::create <maildirMessage>
+ (thisWeakRef().dynamicCast <maildirFolder>(), i));
+ }
+
+ return (v);
+}
+
+
+std::vector <ref <message> > maildirFolder::getMessages(const std::vector <int>& nums)
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <ref <message> > v;
+
+ for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ {
+ v.push_back(vmime::create <maildirMessage>
+ (thisWeakRef().dynamicCast <maildirFolder>(), *it));
+ }
+
+ return (v);
+}
+
+
+const int maildirFolder::getMessageCount()
+{
+ return (m_messageCount);
+}
+
+
+ref <folder> maildirFolder::getFolder(const folder::path::component& name)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return vmime::create <maildirFolder>(m_path / name, m_store);
+}
+
+
+std::vector <ref <folder> > maildirFolder::getFolders(const bool recursive)
+{
+ if (!isOpen() && !m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ std::vector <ref <folder> > list;
+
+ listFolders(list, recursive);
+
+ return (list);
+}
+
+
+void maildirFolder::listFolders(std::vector <ref <folder> >& list, const bool recursive)
+{
+ try
+ {
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ ref <utility::file> rootDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path,
+ m_path.isEmpty() ? maildirUtils::FOLDER_PATH_ROOT
+ : maildirUtils::FOLDER_PATH_CONTAINER));
+
+ if (rootDir->exists())
+ {
+ ref <utility::fileIterator> it = rootDir->getFiles();
+
+ while (it->hasMoreElements())
+ {
+ ref <utility::file> file = it->nextElement();
+
+ if (maildirUtils::isSubfolderDirectory(*file))
+ {
+ const utility::path subPath =
+ m_path / file->getFullPath().getLastComponent();
+
+ ref <maildirFolder> subFolder =
+ vmime::create <maildirFolder>(subPath, m_store);
+
+ list.push_back(subFolder);
+
+ if (recursive)
+ subFolder->listFolders(list, true);
+ }
+ }
+ }
+ else
+ {
+ // No sub-folder
+ }
+ }
+ catch (exceptions::filesystem_exception& e)
+ {
+ throw exceptions::command_error("LIST", "", "", e);
+ }
+}
+
+
+void maildirFolder::rename(const folder::path& newPath)
+{
+ if (!m_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_store->isValidFolderName(newPath.getLastComponent()))
+ throw exceptions::invalid_folder_name();
+
+ // Rename the directory on the file system
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ ref <utility::file> rootDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT));
+ ref <utility::file> contDir = fsf->create
+ (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER));
+
+ try
+ {
+ const utility::file::path newRootPath =
+ maildirUtils::getFolderFSPath(m_store, newPath, maildirUtils::FOLDER_PATH_ROOT);
+ const utility::file::path newContPath =
+ maildirUtils::getFolderFSPath(m_store, newPath, maildirUtils::FOLDER_PATH_CONTAINER);
+
+ 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 =
+ maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT);
+ const utility::file::path contPath =
+ maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER);
+
+ try
+ {
+ rootDir->rename(rootPath);
+ contDir->rename(contPath);
+ }
+ catch (exceptions::filesystem_exception& e)
+ {
+ // Ignore
+ }
+
+ throw exceptions::command_error("RENAME", "", "", e);
+ }
+
+ // Notify folder renamed
+ folder::path oldPath(m_path);
+
+ m_path = newPath;
+ m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, newPath);
+
+ notifyFolder(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == oldPath)
+ {
+ (*it)->m_path = newPath;
+ (*it)->m_name = newPath.getLastComponent();
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ 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);
+
+ events::folderEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::folderEvent::TYPE_RENAMED, oldPath, (*it)->m_path);
+
+ (*it)->notifyFolder(event);
+ }
+ }
+}
+
+
+void maildirFolder::deleteMessage(const int num)
+{
+ // Mark messages as deleted
+ setMessageFlags(num, num, message::FLAG_MODE_ADD, message::FLAG_DELETED);
+}
+
+
+void maildirFolder::deleteMessages(const int from, const int to)
+{
+ // Mark messages as deleted
+ setMessageFlags(from, to, message::FLAG_MODE_ADD, message::FLAG_DELETED);
+}
+
+
+void maildirFolder::deleteMessages(const std::vector <int>& nums)
+{
+ // Mark messages as deleted
+ setMessageFlags(nums, message::FLAG_MODE_ADD, message::FLAG_DELETED);
+}
+
+
+void maildirFolder::setMessageFlags
+ (const int from, const int to, const int flags, const int mode)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_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");
+
+ // Construct the list of message numbers
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ // Change message flags
+ setMessageFlagsImpl(nums, flags, mode);
+
+ // 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 ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*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 ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*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 ((*it)->getNumber() >= from && (*it)->getNumber() <= to2 &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+
+ // TODO: notify other folders with the same path
+}
+
+
+void maildirFolder::setMessageFlags
+ (const std::vector <int>& nums, const int flags, const int mode)
+{
+ if (!m_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");
+
+ // Sort the list of message numbers
+ std::vector <int> list;
+
+ list.resize(nums.size());
+ std::copy(nums.begin(), nums.end(), list.begin());
+
+ std::sort(list.begin(), list.end());
+
+ // Change message flags
+ setMessageFlagsImpl(list, flags, mode);
+
+ // 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(list.begin(), list.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(list.begin(), list.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(list.begin(), list.end(), (*it)->getNumber()) &&
+ (*it)->m_flags != message::FLAG_UNDEFINED)
+ {
+ (*it)->m_flags = flags;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ // Notify message flags changed
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+
+ // TODO: notify other folders with the same path
+}
+
+
+void maildirFolder::setMessageFlagsImpl
+ (const std::vector <int>& nums, const int flags, const int mode)
+{
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+
+ 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;
+ ref <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)
+ }
+ }
+}
+
+
+void maildirFolder::addMessage(ref <vmime::message> msg, const int flags,
+ vmime::datetime* date, utility::progressionListener* progress)
+{
+ std::ostringstream oss;
+ utility::outputStreamAdapter ossAdapter(oss);
+
+ msg->generate(ossAdapter);
+
+ const std::string& str = oss.str();
+ utility::inputStreamStringAdapter strAdapter(str);
+
+ addMessage(strAdapter, str.length(), flags, date, progress);
+}
+
+
+void maildirFolder::addMessage(utility::inputStream& is, const int size,
+ const int flags, vmime::datetime* /* date */, utility::progressionListener* progress)
+{
+ if (!m_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");
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ utility::file::path tmpDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_TMP);
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+
+ const utility::file::path::component filename =
+ maildirUtils::buildFilename(maildirUtils::generateId(),
+ ((flags == message::FLAG_UNDEFINED) ? 0 : flags));
+
+ try
+ {
+ ref <utility::file> tmpDir = fsf->create(tmpDirPath);
+ tmpDir->createDirectory(true);
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Don't throw now, it will fail later...
+ }
+
+ try
+ {
+ ref <utility::file> curDir = fsf->create(curDirPath);
+ curDir->createDirectory(true);
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Don't throw now, it will fail later...
+ }
+
+ // Actually add the message
+ copyMessageImpl(tmpDirPath, curDirPath, 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);
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_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());
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+void maildirFolder::copyMessageImpl(const utility::file::path& tmpDirPath,
+ const utility::file::path& curDirPath, const utility::file::path::component& filename,
+ utility::inputStream& is, const utility::stream::size_type size,
+ utility::progressionListener* progress)
+{
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ ref <utility::file> file = fsf->create(tmpDirPath / filename);
+
+ if (progress)
+ progress->start(size);
+
+ // First, write the message into 'tmp'...
+ try
+ {
+ file->createFile();
+
+ ref <utility::fileWriter> fw = file->getFileWriter();
+ ref <utility::outputStream> os = fw->getOutputStream();
+
+ utility::stream::value_type buffer[65536];
+ utility::stream::size_type total = 0;
+
+ while (!is.eof())
+ {
+ const utility::stream::size_type read = is.read(buffer, sizeof(buffer));
+
+ if (read != 0)
+ {
+ os->write(buffer, read);
+ total += read;
+ }
+
+ if (progress)
+ progress->progress(total, size);
+ }
+ }
+ catch (exception& e)
+ {
+ if (progress)
+ progress->stop(size);
+
+ // Delete temporary file
+ try
+ {
+ ref <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(curDirPath / filename);
+ }
+ catch (exception& e)
+ {
+ if (progress)
+ progress->stop(size);
+
+ // Delete temporary file
+ try
+ {
+ ref <utility::file> file = fsf->create(tmpDirPath / filename);
+ file->remove();
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Ignore
+ }
+
+ throw exceptions::command_error("ADD", "", "", e);
+ }
+
+ if (progress)
+ progress->stop(size);
+}
+
+
+void maildirFolder::copyMessage(const folder::path& dest, const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ copyMessages(dest, num, num);
+}
+
+
+void maildirFolder::copyMessages(const folder::path& dest, const int from, const int to)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ // Construct the list of message numbers
+ const int to2 = (to == -1) ? m_messageCount : to;
+ const int count = to - from + 1;
+
+ std::vector <int> nums;
+ nums.resize(count);
+
+ for (int i = from, j = 0 ; i <= to2 ; ++i, ++j)
+ nums[j] = i;
+
+ // Copy messages
+ copyMessagesImpl(dest, nums);
+}
+
+
+void maildirFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ // Copy messages
+ copyMessagesImpl(dest, nums);
+}
+
+
+void maildirFolder::copyMessagesImpl(const folder::path& dest, const std::vector <int>& nums)
+{
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+
+ utility::file::path destCurDirPath = maildirUtils::getFolderFSPath
+ (m_store, dest, maildirUtils::FOLDER_PATH_CUR);
+ utility::file::path destTmpDirPath = maildirUtils::getFolderFSPath
+ (m_store, dest, maildirUtils::FOLDER_PATH_TMP);
+
+ // Create destination directories
+ try
+ {
+ ref <utility::file> destTmpDir = fsf->create(destTmpDirPath);
+ destTmpDir->createDirectory(true);
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Don't throw now, it will fail later...
+ }
+
+ try
+ {
+ ref <utility::file> destCurDir = fsf->create(destCurDirPath);
+ destCurDir->createDirectory(true);
+ }
+ catch (exceptions::filesystem_exception&)
+ {
+ // Don't throw now, it will fail later...
+ }
+
+ // Copy messages
+ 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);
+
+ ref <utility::file> file = fsf->create(curDirPath / msg.path);
+ ref <utility::fileReader> fr = file->getFileReader();
+ ref <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)
+{
+ for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_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)
+{
+ const int oldCount = m_messageCount;
+
+ scanFolder();
+
+ count = m_messageCount;
+ unseen = m_unreadMessageCount;
+
+ // Notify message count changed (new messages)
+ 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;
+
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_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());
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+}
+
+
+void maildirFolder::expunge()
+{
+ if (!m_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");
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+
+ 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
+ {
+ ref <utility::file> file = fsf->create(curDirPath / infos.path);
+ file->remove();
+ }
+ catch (exceptions::filesystem_exception& e)
+ {
+ // Ignore (not important)
+ }
+ }
+ }
+
+ if (!nums.empty())
+ {
+ for (int i = nums.size() - 1 ; i >= 0 ; --i)
+ m_messageInfos.erase(m_messageInfos.begin() + i);
+ }
+
+ m_messageCount -= nums.size();
+ m_unreadMessageCount -= unreadCount;
+
+ // Notify message expunged
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_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());
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_REMOVED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+}
+
+
+ref <folder> maildirFolder::getParent()
+{
+ if (m_path.isEmpty())
+ return NULL;
+ else
+ return vmime::create <maildirFolder>(m_path.getParent(), m_store);
+}
+
+
+weak_ref <const store> maildirFolder::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <store> maildirFolder::getStore()
+{
+ return (m_store);
+}
+
+
+void maildirFolder::fetchMessages(std::vector <ref <message> >& msg,
+ const int options, utility::progressionListener* progress)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ const int total = msg.size();
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ weak_ref <maildirFolder> _this = thisWeakRef().dynamicCast <maildirFolder>();
+
+ for (std::vector <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ (*it).dynamicCast <maildirMessage>()->fetch(_this, options);
+
+ if (progress)
+ progress->progress(++current, total);
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+void maildirFolder::fetchMessage(ref <message> msg, const int options)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ msg.dynamicCast <maildirMessage>()->fetch
+ (thisWeakRef().dynamicCast <maildirFolder>(), options);
+}
+
+
+const int maildirFolder::getFetchCapabilities() const
+{
+ return (FETCH_ENVELOPE | FETCH_STRUCTURE | FETCH_CONTENT_INFO |
+ FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID |
+ FETCH_IMPORTANCE);
+}
+
+
+const utility::file::path maildirFolder::getMessageFSPath(const int number) const
+{
+ utility::file::path curDirPath = maildirUtils::getFolderFSPath
+ (m_store, m_path, maildirUtils::FOLDER_PATH_CUR);
+
+ return (curDirPath / m_messageInfos[number - 1].path);
+}
+
+
+} // maildir
+} // net
+} // vmime
diff --git a/src/net/maildir/maildirMessage.cpp b/src/net/maildir/maildirMessage.cpp
new file mode 100644
index 00000000..358b6046
--- /dev/null
+++ b/src/net/maildir/maildirMessage.cpp
@@ -0,0 +1,505 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/maildir/maildirMessage.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/platformDependant.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+//
+// maildirPart
+//
+
+class maildirStructure;
+
+class maildirPart : public part
+{
+public:
+
+ maildirPart(weak_ref <maildirPart> parent, const int number, const bodyPart& part);
+ ~maildirPart();
+
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ weak_ref <const maildirPart> getParent() const { return (m_parent); }
+
+ const mediaType& getType() const { return (m_mediaType); }
+ const int getSize() const { return (m_size); }
+ const int getNumber() const { return (m_number); }
+
+ const header& getHeader() const
+ {
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+ else
+ return (*m_header);
+ }
+
+ header& getOrCreateHeader()
+ {
+ if (m_header != NULL)
+ return (*m_header);
+ else
+ return (*(m_header = vmime::create <header>()));
+ }
+
+ const int getHeaderParsedOffset() const { return (m_headerParsedOffset); }
+ const int getHeaderParsedLength() const { return (m_headerParsedLength); }
+
+ const int getBodyParsedOffset() const { return (m_bodyParsedOffset); }
+ const int getBodyParsedLength() const { return (m_bodyParsedLength); }
+
+private:
+
+ ref <maildirStructure> m_structure;
+ weak_ref <maildirPart> m_parent;
+ ref <header> m_header;
+
+ int m_number;
+ int m_size;
+ mediaType m_mediaType;
+
+ int m_headerParsedOffset;
+ int m_headerParsedLength;
+
+ int m_bodyParsedOffset;
+ int m_bodyParsedLength;
+};
+
+
+
+//
+// maildirStructure
+//
+
+class maildirStructure : public structure
+{
+private:
+
+ maildirStructure()
+ {
+ }
+
+public:
+
+ maildirStructure(weak_ref <maildirPart> parent, const bodyPart& part)
+ {
+ m_parts.push_back(vmime::create <maildirPart>(parent, 1, part));
+ }
+
+ maildirStructure(weak_ref <maildirPart> parent, const std::vector <ref <const vmime::bodyPart> >& list)
+ {
+ int number = 1;
+
+ for (unsigned int i = 0 ; i < list.size() ; ++i)
+ m_parts.push_back(vmime::create <maildirPart>(parent, number, *list[i]));
+ }
+
+
+ const part& operator[](const int x) const
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ part& operator[](const int x)
+ {
+ return (*m_parts[x - 1]);
+ }
+
+ const int getCount() const
+ {
+ return (m_parts.size());
+ }
+
+
+ static maildirStructure* emptyStructure()
+ {
+ return (&m_emptyStructure);
+ }
+
+private:
+
+ static maildirStructure m_emptyStructure;
+
+ std::vector <ref <maildirPart> > m_parts;
+};
+
+
+maildirStructure maildirStructure::m_emptyStructure;
+
+
+
+maildirPart::maildirPart(weak_ref <maildirPart> parent, const int number, const bodyPart& part)
+ : m_parent(parent), m_header(NULL), m_number(number)
+{
+ if (part.getBody()->getPartList().size() == 0)
+ m_structure = NULL;
+ else
+ {
+ m_structure = vmime::create <maildirStructure>
+ (thisWeakRef().dynamicCast <maildirPart>(),
+ part.getBody()->getPartList());
+ }
+
+ 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();
+}
+
+
+maildirPart::~maildirPart()
+{
+}
+
+
+const structure& maildirPart::getStructure() const
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*maildirStructure::emptyStructure());
+}
+
+
+structure& maildirPart::getStructure()
+{
+ if (m_structure != NULL)
+ return (*m_structure);
+ else
+ return (*maildirStructure::emptyStructure());
+}
+
+
+
+//
+// maildirMessage
+//
+
+maildirMessage::maildirMessage(weak_ref <maildirFolder> folder, const int num)
+ : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED),
+ m_expunged(false), m_structure(NULL)
+{
+ m_folder->registerMessage(this);
+}
+
+
+maildirMessage::~maildirMessage()
+{
+ if (m_folder)
+ m_folder->unregisterMessage(this);
+}
+
+
+void maildirMessage::onFolderClosed()
+{
+ m_folder = NULL;
+}
+
+
+const int maildirMessage::getNumber() const
+{
+ return (m_num);
+}
+
+
+const message::uid maildirMessage::getUniqueId() const
+{
+ return (m_uid);
+}
+
+
+const int maildirMessage::getSize() const
+{
+ if (m_size == -1)
+ throw exceptions::unfetched_object();
+
+ return (m_size);
+}
+
+
+const bool maildirMessage::isExpunged() const
+{
+ return (m_expunged);
+}
+
+
+const structure& maildirMessage::getStructure() const
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+structure& maildirMessage::getStructure()
+{
+ if (m_structure == NULL)
+ throw exceptions::unfetched_object();
+
+ return (*m_structure);
+}
+
+
+ref <const header> maildirMessage::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+
+ return (m_header);
+}
+
+
+const 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)
+{
+ if (!m_folder)
+ throw exceptions::folder_not_found();
+
+ m_folder->setMessageFlags(m_num, m_num, flags, mode);
+}
+
+
+void maildirMessage::extract(utility::outputStream& os,
+ utility::progressionListener* progress, const int start,
+ const int length, const bool peek) const
+{
+ extractImpl(os, progress, 0, m_size, start, length, peek);
+}
+
+
+void maildirMessage::extractPart(const part& p, utility::outputStream& os,
+ utility::progressionListener* progress, const int start,
+ const int length, const bool peek) const
+{
+ const maildirPart& mp = dynamic_cast <const maildirPart&>(p);
+
+ extractImpl(os, progress, mp.getBodyParsedOffset(), mp.getBodyParsedLength(),
+ start, length, peek);
+}
+
+
+void maildirMessage::extractImpl(utility::outputStream& os, utility::progressionListener* progress,
+ const int start, const int length, const int partialStart, const int partialLength,
+ const bool /* peek */) const
+{
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ const utility::file::path path = m_folder->getMessageFSPath(m_num);
+ ref <utility::file> file = fsf->create(path);
+
+ ref <utility::fileReader> reader = file->getFileReader();
+ ref <utility::inputStream> is = reader->getInputStream();
+
+ is->skip(start + partialStart);
+
+ utility::stream::value_type buffer[8192];
+ utility::stream::size_type remaining = (partialLength == -1 ? length
+ : std::min(partialLength, length));
+
+ const int total = remaining;
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ while (!is->eof() && remaining > 0)
+ {
+ const utility::stream::size_type 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(part& p)
+{
+ maildirPart& mp = dynamic_cast <maildirPart&>(p);
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ const utility::file::path path = m_folder->getMessageFSPath(m_num);
+ ref <utility::file> file = fsf->create(path);
+
+ ref <utility::fileReader> reader = file->getFileReader();
+ ref <utility::inputStream> is = reader->getInputStream();
+
+ is->skip(mp.getHeaderParsedOffset());
+
+ utility::stream::value_type buffer[1024];
+ utility::stream::size_type remaining = mp.getHeaderParsedLength();
+
+ string contents;
+ contents.reserve(remaining);
+
+ while (!is->eof() && remaining > 0)
+ {
+ const utility::stream::size_type read =
+ is->read(buffer, std::min(remaining, sizeof(buffer)));
+
+ remaining -= read;
+
+ contents.append(buffer, read);
+ }
+
+ mp.getOrCreateHeader().parse(contents);
+}
+
+
+void maildirMessage::fetch(weak_ref <maildirFolder> folder, const int options)
+{
+ if (m_folder != folder)
+ throw exceptions::folder_not_found();
+
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ const utility::file::path path = folder->getMessageFSPath(m_num);
+ ref <utility::file> file = fsf->create(path);
+
+ if (options & folder::FETCH_FLAGS)
+ m_flags = maildirUtils::extractFlags(path.getLastComponent());
+
+ if (options & folder::FETCH_SIZE)
+ m_size = file->getLength();
+
+ if (options & folder::FETCH_UID)
+ m_uid = maildirUtils::extractId(path.getLastComponent()).getBuffer();
+
+ if (options & (folder::FETCH_ENVELOPE | folder::FETCH_CONTENT_INFO |
+ folder::FETCH_FULL_HEADER | folder::FETCH_STRUCTURE |
+ folder::FETCH_IMPORTANCE))
+ {
+ string contents;
+
+ ref <utility::fileReader> reader = file->getFileReader();
+ ref <utility::inputStream> is = reader->getInputStream();
+
+ // Need whole message contents for structure
+ if (options & folder::FETCH_STRUCTURE)
+ {
+ utility::stream::value_type buffer[16384];
+
+ contents.reserve(file->getLength());
+
+ while (!is->eof())
+ {
+ const utility::stream::size_type read = is->read(buffer, sizeof(buffer));
+ contents.append(buffer, read);
+ }
+ }
+ // Need only header
+ else
+ {
+ utility::stream::value_type buffer[1024];
+
+ contents.reserve(4096);
+
+ while (!is->eof())
+ {
+ const utility::stream::size_type read = is->read(buffer, sizeof(buffer));
+ contents.append(buffer, read);
+
+ const string::size_type sep1 = contents.rfind("\r\n\r\n");
+ const string::size_type 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 & folder::FETCH_STRUCTURE)
+ {
+ m_structure = vmime::create <maildirStructure>(null, msg);
+ }
+
+ // Extract some header fields or whole header
+ if (options & (folder::FETCH_ENVELOPE |
+ folder::FETCH_CONTENT_INFO |
+ folder::FETCH_FULL_HEADER |
+ folder::FETCH_IMPORTANCE))
+ {
+ getOrCreateHeader()->copyFrom(*(msg.getHeader()));
+ }
+ }
+}
+
+
+ref <header> maildirMessage::getOrCreateHeader()
+{
+ if (m_header != NULL)
+ return (m_header);
+ else
+ return (m_header = vmime::create <header>());
+}
+
+
+} // maildir
+} // net
+} // vmime
diff --git a/src/net/maildir/maildirStore.cpp b/src/net/maildir/maildirStore.cpp
new file mode 100644
index 00000000..731024fa
--- /dev/null
+++ b/src/net/maildir/maildirStore.cpp
@@ -0,0 +1,250 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/maildir/maildirStore.hpp"
+
+#include "vmime/net/maildir/maildirFolder.hpp"
+
+#include "vmime/utility/smartPtr.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+maildirStore::maildirStore(ref <session> sess, ref <authenticator> auth)
+ : store(sess, getInfosInstance(), auth), m_connected(false)
+{
+}
+
+
+maildirStore::~maildirStore()
+{
+ if (isConnected())
+ disconnect();
+}
+
+
+const string maildirStore::getProtocolName() const
+{
+ return "maildir";
+}
+
+
+ref <folder> maildirStore::getRootFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <maildirFolder>(folder::path(),
+ thisWeakRef().dynamicCast <maildirStore>());
+}
+
+
+ref <folder> maildirStore::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <maildirFolder>(folder::path::component("inbox"),
+ thisWeakRef().dynamicCast <maildirStore>());
+}
+
+
+ref <folder> maildirStore::getFolder(const folder::path& path)
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <maildirFolder>(path,
+ thisWeakRef().dynamicCast <maildirStore>());
+}
+
+
+const bool maildirStore::isValidFolderName(const folder::path::component& name) const
+{
+ if (!platformDependant::getHandler()->getFileSystemFactory()->isValidPathComponent(name))
+ return false;
+
+ const string& buf = name.getBuffer();
+
+ // Name cannot start/end with spaces
+ if (utility::stringUtils::trim(buf) != name.getBuffer())
+ return false;
+
+ // Name cannot start with '.'
+ const int 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
+ utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory();
+
+ m_fsPath = fsf->stringToPath(GET_PROPERTY(string, PROPERTY_SERVER_ROOTPATH));
+
+ ref <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_connected = true;
+}
+
+
+const bool maildirStore::isConnected() const
+{
+ return (m_connected);
+}
+
+
+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.
+}
+
+
+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);
+}
+
+
+const 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
+
+maildirStore::_infos maildirStore::sm_infos;
+
+
+const serviceInfos& maildirStore::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& maildirStore::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string maildirStore::_infos::getPropertyPrefix() const
+{
+ return "store.maildir.";
+}
+
+
+const maildirStore::_infos::props& maildirStore::_infos::getProperties() const
+{
+ static props p =
+ {
+ property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED)
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> maildirStore::_infos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // Maildir-specific properties
+ list.push_back(p.PROPERTY_SERVER_ROOTPATH);
+
+ return (list);
+}
+
+
+} // maildir
+} // net
+} // vmime
diff --git a/src/net/maildir/maildirUtils.cpp b/src/net/maildir/maildirUtils.cpp
new file mode 100644
index 00000000..b0f43533
--- /dev/null
+++ b/src/net/maildir/maildirUtils.cpp
@@ -0,0 +1,208 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/maildir/maildirUtils.hpp"
+#include "vmime/net/maildir/maildirStore.hpp"
+
+#include "vmime/utility/random.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+const vmime::word maildirUtils::TMP_DIR("tmp", vmime::charset(vmime::charsets::US_ASCII)); // ensure reliable delivery (not to be listed)
+const vmime::word maildirUtils::CUR_DIR("cur", vmime::charset(vmime::charsets::US_ASCII)); // no longer new messages
+const vmime::word maildirUtils::NEW_DIR("new", vmime::charset(vmime::charsets::US_ASCII)); // unread messages
+
+
+const utility::file::path maildirUtils::getFolderFSPath
+ (weak_ref <maildirStore> store, const utility::path& folderPath, const FolderFSPathMode mode)
+{
+ // Root path
+ utility::file::path path(store->getFileSystemPath());
+
+ const int count = (mode == FOLDER_PATH_CONTAINER
+ ? folderPath.getSize() : folderPath.getSize() - 1);
+
+ // Parent folders
+ for (int i = 0 ; i < count ; ++i)
+ {
+ utility::file::path::component comp(folderPath[i]);
+
+ // TODO: may not work with all encodings...
+ comp.setBuffer("." + comp.getBuffer() + ".directory");
+
+ path /= comp;
+ }
+
+ // Last component
+ if (folderPath.getSize() != 0 &&
+ mode != FOLDER_PATH_CONTAINER)
+ {
+ path /= folderPath.getLastComponent();
+
+ switch (mode)
+ {
+ case FOLDER_PATH_ROOT: break; // Nothing to do
+ case FOLDER_PATH_NEW: path /= NEW_DIR; break;
+ case FOLDER_PATH_CUR: path /= CUR_DIR; break;
+ case FOLDER_PATH_TMP: path /= TMP_DIR; break;
+ case FOLDER_PATH_CONTAINER: break; // Can't happen...
+ }
+ }
+
+ return (path);
+}
+
+
+const bool maildirUtils::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);
+}
+
+
+const 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);
+}
+
+
+const utility::file::path::component maildirUtils::extractId
+ (const utility::file::path::component& filename)
+{
+ string::size_type sep = filename.getBuffer().rfind(':');
+ if (sep == string::npos) return (filename);
+
+ return (utility::path::component
+ (string(filename.getBuffer().begin(), filename.getBuffer().begin() + sep)));
+}
+
+
+const int maildirUtils::extractFlags(const utility::file::path::component& comp)
+{
+ string::size_type sep = comp.getBuffer().rfind(':');
+ if (sep == string::npos) return (0);
+
+ const string flagsString(comp.getBuffer().begin() + sep + 1, comp.getBuffer().end());
+ const string::size_type count = flagsString.length();
+
+ int flags = 0;
+
+ for (string::size_type 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;
+ }
+ }
+
+ 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";
+
+ return (utility::file::path::component(str));
+}
+
+
+const utility::file::path::component maildirUtils::buildFilename
+ (const utility::file::path::component& id, const int flags)
+{
+ return (buildFilename(id, buildFlags(flags)));
+}
+
+
+const utility::file::path::component maildirUtils::buildFilename
+ (const utility::file::path::component& id, const utility::file::path::component& flags)
+{
+ return (utility::path::component(id.getBuffer() + ":" + flags.getBuffer()));
+}
+
+
+const utility::file::path::component maildirUtils::generateId()
+{
+ std::ostringstream oss;
+
+ oss << utility::random::getTime();
+ oss << ".";
+ oss << utility::random::getProcess();
+ oss << ".";
+ oss << utility::random::getString(6);
+
+ return (utility::file::path::component(oss.str()));
+}
+
+
+
+//
+// messageIdComparator
+//
+
+maildirUtils::messageIdComparator::messageIdComparator
+ (const utility::file::path::component& comp)
+ : m_comp(maildirUtils::extractId(comp))
+{
+}
+
+
+const bool maildirUtils::messageIdComparator::operator()
+ (const utility::file::path::component& other) const
+{
+ return (m_comp == maildirUtils::extractId(other));
+}
+
+
+} // maildir
+} // net
+} // vmime
diff --git a/src/net/message.cpp b/src/net/message.cpp
new file mode 100644
index 00000000..0c55d1da
--- /dev/null
+++ b/src/net/message.cpp
@@ -0,0 +1,46 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/message.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+const part& part::operator[](const int x) const
+{
+ return (getStructure()[x]);
+}
+
+
+part& part::operator[](const int x)
+{
+ return (getStructure()[x]);
+}
+
+
+const int part::getCount() const
+{
+ return (getStructure().getCount());
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp
new file mode 100644
index 00000000..807f9b6c
--- /dev/null
+++ b/src/net/pop3/POP3Folder.cpp
@@ -0,0 +1,826 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/pop3/POP3Folder.hpp"
+
+#include "vmime/net/pop3/POP3Store.hpp"
+#include "vmime/net/pop3/POP3Message.hpp"
+
+#include "vmime/exception.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3Folder::POP3Folder(const folder::path& path, POP3Store* store)
+ : m_store(store), m_path(path),
+ m_name(path.isEmpty() ? folder::path::component("") : path.getLastComponent()),
+ m_mode(-1), m_open(false)
+{
+ m_store->registerFolder(this);
+}
+
+
+POP3Folder::~POP3Folder()
+{
+ if (m_store)
+ {
+ if (m_open)
+ close(false);
+
+ m_store->unregisterFolder(this);
+ }
+ else if (m_open)
+ {
+ onClose();
+ }
+}
+
+
+const int POP3Folder::getMode() const
+{
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_mode);
+}
+
+
+const 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();
+}
+
+
+const 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)
+{
+ if (!m_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")
+ {
+ m_store->sendRequest("STAT");
+
+ string response;
+ m_store->readResponse(response, false);
+
+ if (!m_store->isSuccessResponse(response))
+ throw exceptions::command_error("STAT", response);
+
+ m_store->stripResponseCode(response, response);
+
+ std::istringstream iss(response);
+ iss >> m_messageCount;
+
+ if (iss.fail())
+ throw exceptions::invalid_response("STAT", response);
+
+ m_open = true;
+ m_mode = mode;
+ }
+ else
+ {
+ throw exceptions::folder_not_found();
+ }
+}
+
+void POP3Folder::close(const bool expunge)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ if (!expunge)
+ {
+ m_store->sendRequest("RSET");
+
+ string response;
+ m_store->readResponse(response, false);
+ }
+
+ 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();
+}
+
+
+const bool POP3Folder::exists()
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return (m_path.isEmpty() || (m_path.getSize() == 1 && m_path[0].getBuffer() == "INBOX"));
+}
+
+
+const bool POP3Folder::isOpen() const
+{
+ return (m_open);
+}
+
+
+ref <message> POP3Folder::getMessage(const int num)
+{
+ if (!m_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 vmime::create <POP3Message>(this, num);
+}
+
+
+std::vector <ref <message> > POP3Folder::getMessages(const int from, const int to)
+{
+ const int to2 = (to == -1 ? m_messageCount : to);
+
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+ else if (to2 < from || from < 1 || to2 < 1 || from > m_messageCount || to2 > m_messageCount)
+ throw exceptions::message_not_found();
+
+ std::vector <ref <message> > v;
+
+ for (int i = from ; i <= to2 ; ++i)
+ v.push_back(vmime::create <POP3Message>(this, i));
+
+ return (v);
+}
+
+
+std::vector <ref <message> > POP3Folder::getMessages(const std::vector <int>& nums)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::vector <ref <message> > v;
+
+ for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it)
+ {
+ if (*it < 1|| *it > m_messageCount)
+ throw exceptions::message_not_found();
+
+ v.push_back(vmime::create <POP3Message>(this, *it));
+ }
+
+ return (v);
+}
+
+
+const int POP3Folder::getMessageCount()
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ return (m_messageCount);
+}
+
+
+ref <folder> POP3Folder::getFolder(const folder::path::component& name)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ return vmime::create <POP3Folder>(m_path / name, m_store);
+}
+
+
+std::vector <ref <folder> > POP3Folder::getFolders(const bool /* recursive */)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (m_path.isEmpty())
+ {
+ std::vector <ref <folder> > v;
+ v.push_back(vmime::create <POP3Folder>(folder::path::component("INBOX"), m_store));
+ return (v);
+ }
+ else
+ {
+ std::vector <ref <folder> > v;
+ return (v);
+ }
+}
+
+
+void POP3Folder::fetchMessages(std::vector <ref <message> >& msg, const int options,
+ utility::progressionListener* progress)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ const int total = msg.size();
+ int current = 0;
+
+ if (progress)
+ progress->start(total);
+
+ for (std::vector <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ (*it).dynamicCast <POP3Message>()->fetch(this, options);
+
+ if (progress)
+ progress->progress(++current, total);
+ }
+
+ if (options & FETCH_SIZE)
+ {
+ // Send the "LIST" command
+ std::ostringstream command;
+ command << "LIST";
+
+ m_store->sendRequest(command.str());
+
+ // Get the response
+ string response;
+ m_store->readResponse(response, true, NULL);
+
+ if (m_store->isSuccessResponse(response))
+ {
+ m_store->stripFirstLine(response, response, NULL);
+
+ // C: LIST
+ // S: +OK
+ // S: 1 47548
+ // S: 2 12653
+ // S: .
+ std::map <int, string> result;
+ parseMultiListOrUidlResponse(response, result);
+
+ for (std::vector <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ ref <POP3Message> m = (*it).dynamicCast <POP3Message>();
+
+ std::map <int, string>::const_iterator x = result.find(m->m_num);
+
+ if (x != result.end())
+ {
+ int size = 0;
+
+ std::istringstream iss((*x).second);
+ iss >> size;
+
+ m->m_size = size;
+ }
+ }
+ }
+
+ }
+
+ if (options & FETCH_UID)
+ {
+ // Send the "UIDL" command
+ std::ostringstream command;
+ command << "UIDL";
+
+ m_store->sendRequest(command.str());
+
+ // Get the response
+ string response;
+ m_store->readResponse(response, true, NULL);
+
+ if (m_store->isSuccessResponse(response))
+ {
+ m_store->stripFirstLine(response, response, NULL);
+
+ // C: UIDL
+ // S: +OK
+ // S: 1 whqtswO00WBw418f9t5JxYwZ
+ // S: 2 QhdPYR:00WBw1Ph7x7
+ // S: .
+ std::map <int, string> result;
+ parseMultiListOrUidlResponse(response, result);
+
+ for (std::vector <ref <message> >::iterator it = msg.begin() ;
+ it != msg.end() ; ++it)
+ {
+ ref <POP3Message> m = (*it).dynamicCast <POP3Message>();
+
+ 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(ref <message> msg, const int options)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ msg.dynamicCast <POP3Message>()->fetch(this, options);
+
+ if (options & FETCH_SIZE)
+ {
+ // Send the "LIST" command
+ std::ostringstream command;
+ command << "LIST " << msg->getNumber();
+
+ m_store->sendRequest(command.str());
+
+ // Get the response
+ string response;
+ m_store->readResponse(response, false, NULL);
+
+ if (m_store->isSuccessResponse(response))
+ {
+ m_store->stripResponseCode(response, response);
+
+ // C: LIST 2
+ // S: +OK 2 4242
+ string::iterator it = response.begin();
+
+ while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it;
+ while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+
+ if (it != response.end())
+ {
+ int size = 0;
+
+ std::istringstream iss(string(it, response.end()));
+ iss >> size;
+
+ msg.dynamicCast <POP3Message>()->m_size = size;
+ }
+ }
+ }
+
+ if (options & FETCH_UID)
+ {
+ // Send the "UIDL" command
+ std::ostringstream command;
+ command << "UIDL " << msg->getNumber();
+
+ m_store->sendRequest(command.str());
+
+ // Get the response
+ string response;
+ m_store->readResponse(response, false, NULL);
+
+ if (m_store->isSuccessResponse(response))
+ {
+ m_store->stripResponseCode(response, response);
+
+ // C: UIDL 2
+ // S: +OK 2 QhdPYR:00WBw1Ph7x7
+ string::iterator it = response.begin();
+
+ while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+ while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it;
+ while (it != response.end() && (*it == ' ' || *it == '\t')) ++it;
+
+ if (it != response.end())
+ {
+ msg.dynamicCast <POP3Message>()->m_uid =
+ string(it, response.end());
+ }
+ }
+ }
+}
+
+
+const int POP3Folder::getFetchCapabilities() const
+{
+ return (FETCH_ENVELOPE | FETCH_CONTENT_INFO |
+ FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID |
+ FETCH_IMPORTANCE);
+}
+
+
+ref <folder> POP3Folder::getParent()
+{
+ if (m_path.isEmpty())
+ return NULL;
+ else
+ return vmime::create <POP3Folder>(m_path.getParent(), m_store);
+}
+
+
+weak_ref <const store> POP3Folder::getStore() const
+{
+ return (m_store);
+}
+
+
+weak_ref <store> POP3Folder::getStore()
+{
+ return (m_store);
+}
+
+
+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 = NULL;
+}
+
+
+void POP3Folder::deleteMessage(const int num)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ std::ostringstream command;
+ command << "DELE " << num;
+
+ m_store->sendRequest(command.str());
+
+ string response;
+ m_store->readResponse(response, false);
+
+ if (!m_store->isSuccessResponse(response))
+ throw exceptions::command_error("DELE", response);
+
+ // Update local flags
+ for (std::map <POP3Message*, int>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ POP3Message* msg = (*it).first;
+
+ if (msg->getNumber() == num)
+ msg->m_deleted = true;
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+ nums.push_back(num);
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void POP3Folder::deleteMessages(const int from, const int to)
+{
+ if (from < 1 || (to < from && to != -1))
+ throw exceptions::invalid_argument();
+
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ const int to2 = (to == -1 ? m_messageCount : to);
+
+ for (int i = from ; i <= to2 ; ++i)
+ {
+ std::ostringstream command;
+ command << "DELE " << i;
+
+ m_store->sendRequest(command.str());
+
+ string response;
+ m_store->readResponse(response, false);
+
+ if (!m_store->isSuccessResponse(response))
+ throw exceptions::command_error("DELE", response);
+ }
+
+ // Update local flags
+ for (std::map <POP3Message*, int>::iterator it =
+ m_messages.begin() ; it != m_messages.end() ; ++it)
+ {
+ POP3Message* msg = (*it).first;
+
+ if (msg->getNumber() >= from && msg->getNumber() <= to2)
+ msg->m_deleted = true;
+ }
+
+ // Notify message flags changed
+ std::vector <int> nums;
+
+ for (int i = from ; i <= to2 ; ++i)
+ nums.push_back(i);
+
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, nums);
+
+ notifyMessageChanged(event);
+}
+
+
+void POP3Folder::deleteMessages(const std::vector <int>& nums)
+{
+ if (nums.empty())
+ throw exceptions::invalid_argument();
+
+ if (!m_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)
+ {
+ std::ostringstream command;
+ command << "DELE " << (*it);
+
+ m_store->sendRequest(command.str());
+
+ string response;
+ m_store->readResponse(response, false);
+
+ if (!m_store->isSuccessResponse(response))
+ throw exceptions::command_error("DELE", response);
+ }
+
+ // 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
+ events::messageChangedEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageChangedEvent::TYPE_FLAGS, list);
+
+ notifyMessageChanged(event);
+}
+
+
+void POP3Folder::setMessageFlags(const int /* from */, const int /* to */,
+ const int /* flags */, const int /* mode */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::setMessageFlags(const std::vector <int>& /* nums */,
+ 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(ref <vmime::message> /* msg */, const int /* flags */,
+ vmime::datetime* /* date */, utility::progressionListener* /* progress */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */,
+ vmime::datetime* /* date */, utility::progressionListener* /* progress */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::copyMessage(const folder::path& /* dest */, const int /* num */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::copyMessages(const folder::path& /* dest */, const int /* from */, const int /* to */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector <int>& /* nums */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Folder::status(int& count, int& unseen)
+{
+ if (!m_store)
+ throw exceptions::illegal_state("Store disconnected");
+ else if (!isOpen())
+ throw exceptions::illegal_state("Folder not open");
+
+ m_store->sendRequest("STAT");
+
+ string response;
+ m_store->readResponse(response, false);
+
+ if (!m_store->isSuccessResponse(response))
+ throw exceptions::command_error("STAT", response);
+
+ m_store->stripResponseCode(response, response);
+
+ std::istringstream iss(response);
+ iss >> count;
+
+ unseen = 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
+ events::messageCountEvent event
+ (thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ notifyMessageCount(event);
+
+ // Notify folders with the same path
+ for (std::list <POP3Folder*>::iterator it = m_store->m_folders.begin() ;
+ it != m_store->m_folders.end() ; ++it)
+ {
+ if ((*it) != this && (*it)->getFullPath() == m_path)
+ {
+ (*it)->m_messageCount = count;
+
+ events::messageCountEvent event
+ ((*it)->thisRef().dynamicCast <folder>(),
+ events::messageCountEvent::TYPE_ADDED, nums);
+
+ (*it)->notifyMessageCount(event);
+ }
+ }
+ }
+ }
+}
+
+
+void POP3Folder::expunge()
+{
+ // Not supported by POP3 protocol (deleted messages are automatically
+ // expunged at the end of the session...).
+}
+
+
+void POP3Folder::parseMultiListOrUidlResponse(const string& response, std::map <int, string>& result)
+{
+ std::istringstream iss(response);
+ std::map <int, string> ids;
+
+ string line;
+
+ while (std::getline(iss, line))
+ {
+ 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())));
+ }
+ }
+ }
+}
+
+
+} // pop3
+} // net
+} // vmime
diff --git a/src/net/pop3/POP3Message.cpp b/src/net/pop3/POP3Message.cpp
new file mode 100644
index 00000000..b2e2fae4
--- /dev/null
+++ b/src/net/pop3/POP3Message.cpp
@@ -0,0 +1,213 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/pop3/POP3Message.hpp"
+#include "vmime/net/pop3/POP3Folder.hpp"
+#include "vmime/net/pop3/POP3Store.hpp"
+
+#include <sstream>
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3Message::POP3Message(POP3Folder* folder, const int num)
+ : m_folder(folder), m_num(num), m_size(-1), m_deleted(false)
+{
+ m_folder->registerMessage(this);
+}
+
+
+POP3Message::~POP3Message()
+{
+ if (m_folder)
+ m_folder->unregisterMessage(this);
+}
+
+
+void POP3Message::onFolderClosed()
+{
+ m_folder = NULL;
+}
+
+
+const int POP3Message::getNumber() const
+{
+ return (m_num);
+}
+
+
+const message::uid POP3Message::getUniqueId() const
+{
+ return (m_uid);
+}
+
+
+const int POP3Message::getSize() const
+{
+ if (m_size == -1)
+ throw exceptions::unfetched_object();
+
+ return (m_size);
+}
+
+
+const bool POP3Message::isExpunged() const
+{
+ return (false);
+}
+
+
+const int POP3Message::getFlags() const
+{
+ int flags = FLAG_RECENT;
+
+ if (m_deleted)
+ flags |= FLAG_DELETED;
+
+ return (flags);
+}
+
+
+const structure& POP3Message::getStructure() const
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+structure& POP3Message::getStructure()
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+ref <const header> POP3Message::getHeader() const
+{
+ if (m_header == NULL)
+ throw exceptions::unfetched_object();
+
+ return (m_header);
+}
+
+
+void POP3Message::extract(utility::outputStream& os,
+ utility::progressionListener* progress, const int start,
+ const int length, const bool /* peek */) const
+{
+ if (!m_folder)
+ throw exceptions::illegal_state("Folder closed");
+ else if (!m_folder->m_store)
+ throw exceptions::illegal_state("Store disconnected");
+
+ if (start != 0 && length != -1)
+ throw exceptions::partial_fetch_not_supported();
+
+ // Emit the "RETR" command
+ std::ostringstream oss;
+ oss << "RETR " << m_num;
+
+ const_cast <POP3Folder*>(m_folder)->m_store->sendRequest(oss.str());
+
+ try
+ {
+ POP3Folder::MessageMap::const_iterator it =
+ m_folder->m_messages.find(const_cast <POP3Message*>(this));
+
+ const int totalSize = (it != m_folder->m_messages.end())
+ ? (*it).second : 0;
+
+ const_cast <POP3Folder*>(m_folder)->m_store->
+ readResponse(os, progress, totalSize);
+ }
+ catch (exceptions::command_error& e)
+ {
+ throw exceptions::command_error("RETR", e.response());
+ }
+}
+
+
+void POP3Message::extractPart
+ (const part& /* p */, utility::outputStream& /* os */,
+ utility::progressionListener* /* progress */,
+ const int /* start */, const int /* length */,
+ const bool /* peek */) const
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Message::fetchPartHeader(part& /* p */)
+{
+ throw exceptions::operation_not_supported();
+}
+
+
+void POP3Message::fetch(POP3Folder* folder, const int options)
+{
+ if (m_folder != folder)
+ throw exceptions::folder_not_found();
+
+ // FETCH_STRUCTURE and FETCH_FLAGS are not supported by POP3.
+ if (options & (folder::FETCH_STRUCTURE | folder::FETCH_FLAGS))
+ throw exceptions::operation_not_supported();
+
+ // Check for the real need to fetch the full header
+ static const int optionsRequiringHeader =
+ folder::FETCH_ENVELOPE | folder::FETCH_CONTENT_INFO |
+ folder::FETCH_FULL_HEADER | folder::FETCH_IMPORTANCE;
+
+ if (!(options & optionsRequiringHeader))
+ return;
+
+ // No need to differenciate between FETCH_ENVELOPE,
+ // FETCH_CONTENT_INFO, ... since POP3 only permits to
+ // retrieve the whole header and not fields in particular.
+
+ // Emit the "TOP" command
+ std::ostringstream oss;
+ oss << "TOP " << m_num << " 0";
+
+ m_folder->m_store->sendRequest(oss.str());
+
+ try
+ {
+ string buffer;
+ m_folder->m_store->readResponse(buffer, true);
+
+ m_header = vmime::create <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();
+}
+
+
+} // pop3
+} // net
+} // vmime
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp
new file mode 100644
index 00000000..9ccfa7b7
--- /dev/null
+++ b/src/net/pop3/POP3Store.cpp
@@ -0,0 +1,630 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/pop3/POP3Store.hpp"
+#include "vmime/net/pop3/POP3Folder.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+#include "vmime/messageId.hpp"
+#include "vmime/utility/md5.hpp"
+#include "vmime/utility/filteredStream.hpp"
+
+#include <algorithm>
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3Store::POP3Store(ref <session> sess, ref <authenticator> auth)
+ : store(sess, getInfosInstance(), auth), m_socket(NULL),
+ m_authentified(false), m_timeoutHandler(NULL)
+{
+}
+
+
+POP3Store::~POP3Store()
+{
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+}
+
+
+const string POP3Store::getProtocolName() const
+{
+ return "pop3";
+}
+
+
+ref <folder> POP3Store::getDefaultFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <POP3Folder>(folder::path(folder::path::component("INBOX")), this);
+}
+
+
+ref <folder> POP3Store::getRootFolder()
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <POP3Folder>(folder::path(), this);
+}
+
+
+ref <folder> POP3Store::getFolder(const folder::path& path)
+{
+ if (!isConnected())
+ throw exceptions::illegal_state("Not connected");
+
+ return vmime::create <POP3Folder>(path, this);
+}
+
+
+const bool POP3Store::isValidFolderName(const folder::path::component& /* name */) const
+{
+ return true;
+}
+
+
+void POP3Store::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);
+
+ // Create the time-out handler
+ if (HAS_PROPERTY(PROPERTY_TIMEOUT_FACTORY))
+ {
+ timeoutHandlerFactory* tof = platformDependant::getHandler()->
+ getTimeoutHandlerFactory(GET_PROPERTY(string, PROPERTY_TIMEOUT_FACTORY));
+
+ m_timeoutHandler = tof->create();
+ }
+
+ // Create and connect the socket
+ socketFactory* sf = platformDependant::getHandler()->
+ getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
+
+ m_socket = sf->create();
+ m_socket->connect(address, port);
+
+ // Connection
+ //
+ // eg: C: <connection to server>
+ // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]>
+
+ string response;
+ readResponse(response, false);
+
+ if (isSuccessResponse(response))
+ {
+ bool authentified = false;
+
+ const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
+
+ // Secured authentication with APOP (if requested and if available)
+ //
+ // eg: C: APOP vincent <digest>
+ // --- S: +OK vincent is a valid mailbox
+ messageId mid(response);
+
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
+ {
+ if (mid.getLeft().length() && mid.getRight().length())
+ {
+ // <digest> is the result of MD5 applied to "<message-id>password"
+ sendRequest("APOP " + auth.getUsername() + " "
+ + utility::md5(mid.generate() + auth.getPassword()).hex());
+ readResponse(response, false);
+
+ if (isSuccessResponse(response))
+ {
+ authentified = true;
+ }
+ else
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(response);
+ }
+ }
+ }
+ else
+ {
+ // APOP not supported
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
+ {
+ // Can't fallback on basic authentification
+ internalDisconnect();
+ throw exceptions::unsupported_option();
+ }
+ }
+ }
+
+ if (!authentified)
+ {
+ // 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)
+
+ sendRequest("USER " + auth.getUsername());
+ readResponse(response, false);
+
+ if (isSuccessResponse(response))
+ {
+ sendRequest("PASS " + auth.getPassword());
+ readResponse(response, false);
+
+ if (!isSuccessResponse(response))
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(response);
+ }
+ }
+ else
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(response);
+ }
+ }
+ }
+ else
+ {
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(response);
+ }
+
+ m_authentified = true;
+}
+
+
+const bool POP3Store::isConnected() const
+{
+ return (m_socket && m_socket->isConnected() && m_authentified);
+}
+
+
+void POP3Store::disconnect()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ internalDisconnect();
+}
+
+
+void POP3Store::internalDisconnect()
+{
+ for (std::list <POP3Folder*>::iterator it = m_folders.begin() ;
+ it != m_folders.end() ; ++it)
+ {
+ (*it)->onStoreDisconnected();
+ }
+
+ m_folders.clear();
+
+
+ sendRequest("QUIT");
+
+ m_socket->disconnect();
+ m_socket = NULL;
+
+ m_timeoutHandler = NULL;
+
+ m_authentified = false;
+}
+
+
+void POP3Store::noop()
+{
+ m_socket->send("NOOP");
+
+ string response;
+ readResponse(response, false);
+
+ if (!isSuccessResponse(response))
+ throw exceptions::command_error("NOOP", response);
+}
+
+
+const bool POP3Store::isSuccessResponse(const string& buffer)
+{
+ static const string OK("+OK");
+
+ return (buffer.length() >= 3 &&
+ std::equal(buffer.begin(), buffer.begin() + 3, OK.begin()));
+}
+
+
+const bool POP3Store::stripFirstLine(const string& buffer, string& result, string* firstLine)
+{
+ const string::size_type end = buffer.find('\n');
+
+ if (end != string::npos)
+ {
+ if (firstLine) *firstLine = buffer.substr(0, end);
+ result = buffer.substr(end + 1);
+ return (true);
+ }
+ else
+ {
+ result = buffer;
+ return (false);
+ }
+}
+
+
+void POP3Store::stripResponseCode(const string& buffer, string& result)
+{
+ const string::size_type pos = buffer.find_first_of(" \t");
+
+ if (pos != string::npos)
+ result = buffer.substr(pos + 1);
+ else
+ result = buffer;
+}
+
+
+void POP3Store::sendRequest(const string& buffer, const bool end)
+{
+ if (end)
+ m_socket->send(buffer + "\r\n");
+ else
+ m_socket->send(buffer);
+}
+
+
+void POP3Store::readResponse(string& buffer, const bool multiLine,
+ utility::progressionListener* progress)
+{
+ bool foundTerminator = false;
+ int current = 0, total = 0;
+
+ if (progress)
+ progress->start(total);
+
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ buffer.clear();
+
+ string::value_type last1 = '\0', last2 = '\0';
+
+ for ( ; !foundTerminator ; )
+ {
+#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
+ string receiveBuffer;
+ m_socket->receive(receiveBuffer);
+
+ if (receiveBuffer.empty()) // buffer is empty
+ {
+ platformDependant::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 string::value_type 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 (string::size_type trans ;
+ string::npos != (trans = receiveBuffer.find("\n..")) ; )
+ {
+ receiveBuffer.replace(trans, 3, "\n.");
+ }
+
+ last1 = receiveBuffer[receiveBuffer.length() - 1];
+ last2 = (receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0;
+
+ // Append the data to the response buffer
+ buffer += receiveBuffer;
+ current += receiveBuffer.length();
+
+ // Check for terminator string (and strip it if present)
+ foundTerminator = checkTerminator(buffer, multiLine);
+
+ // Notify progression
+ if (progress)
+ {
+ total = std::max(total, current);
+ progress->progress(current, total);
+ }
+
+ // 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);
+ }
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+void POP3Store::readResponse(utility::outputStream& os,
+ utility::progressionListener* progress, const int predictedSize)
+{
+ int 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
+ utility::stream::value_type buffer[65536];
+ const utility::stream::size_type read = is.read(buffer, sizeof(buffer));
+
+ if (read == 0) // buffer is empty
+ {
+ platformDependant::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data: reset the time-out counter
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ // If we don't have extracted the response code yet
+ if (!codeDone)
+ {
+ temp += string(buffer, read);
+
+ string firstLine;
+
+ if (stripFirstLine(temp, temp, &firstLine) == true)
+ {
+ if (!isSuccessResponse(firstLine))
+ throw exceptions::command_error("?", firstLine);
+
+ codeDone = true;
+
+ os.write(temp.data(), temp.length());
+ temp.clear();
+
+ continue;
+ }
+ }
+ else
+ {
+ // Inject the data into the output stream
+ os.write(buffer, read);
+ current += read;
+
+ // Notify progression
+ if (progress)
+ {
+ total = std::max(total, current);
+ progress->progress(current, total);
+ }
+ }
+ }
+
+ if (progress)
+ progress->stop(total);
+}
+
+
+const bool POP3Store::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);
+}
+
+
+const bool POP3Store::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);
+}
+
+
+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);
+}
+
+
+const int POP3Store::getCapabilities() const
+{
+ return (CAPABILITY_DELETE_MESSAGE);
+}
+
+
+
+// Service infos
+
+POP3Store::_infos POP3Store::sm_infos;
+
+
+const serviceInfos& POP3Store::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& POP3Store::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string POP3Store::_infos::getPropertyPrefix() const
+{
+ return "store.pop3.";
+}
+
+
+const POP3Store::_infos::props& POP3Store::_infos::getProperties() const
+{
+ static props p =
+ {
+ // POP3-specific options
+ property("options.apop", serviceInfos::property::TYPE_BOOL, "false"),
+ property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "false"),
+
+ // Common properties
+ property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "110"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> POP3Store::_infos::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);
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return (list);
+}
+
+
+} // pop3
+} // net
+} // vmime
+
diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp
new file mode 100644
index 00000000..1b6edff9
--- /dev/null
+++ b/src/net/sendmail/sendmailTransport.cpp
@@ -0,0 +1,222 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/sendmail/sendmailTransport.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+#include "vmime/message.hpp"
+#include "vmime/mailboxList.hpp"
+
+#include "vmime/utility/filteredStream.hpp"
+#include "vmime/utility/childProcess.hpp"
+#include "vmime/utility/smartPtr.hpp"
+
+#include "vmime/config.hpp"
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+
+
+#if VMIME_BUILTIN_PLATFORM_POSIX
+
+
+namespace vmime {
+namespace net {
+namespace sendmail {
+
+
+sendmailTransport::sendmailTransport(ref <session> sess, ref <authenticator> auth)
+ : transport(sess, getInfosInstance(), auth), m_connected(false)
+{
+}
+
+
+sendmailTransport::~sendmailTransport()
+{
+ if (isConnected())
+ disconnect();
+}
+
+
+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;
+}
+
+
+const bool sendmailTransport::isConnected() const
+{
+ return (m_connected);
+}
+
+
+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 utility::stream::size_type size,
+ utility::progressionListener* progress)
+{
+ // 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");
+ args.push_back(expeditor.getEmail());
+ args.push_back("--");
+
+ for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
+ args.push_back(recipients.getMailboxAt(i)->getEmail());
+
+ // 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 utility::stream::size_type size, utility::progressionListener* progress)
+{
+ const utility::file::path path = vmime::platformDependant::getHandler()->
+ getFileSystemFactory()->stringToPath(m_sendmailPath);
+
+ ref <utility::childProcess> proc =
+ vmime::platformDependant::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
+
+sendmailTransport::_infos sendmailTransport::sm_infos;
+
+
+const serviceInfos& sendmailTransport::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& sendmailTransport::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string sendmailTransport::_infos::getPropertyPrefix() const
+{
+ return "transport.sendmail.";
+}
+
+
+const sendmailTransport::_infos::props& sendmailTransport::_infos::getProperties() const
+{
+ static props p =
+ {
+ // Path to sendmail (override default)
+ property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH))
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> sendmailTransport::_infos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ list.push_back(p.PROPERTY_BINPATH);
+
+ return (list);
+}
+
+
+} // sendmail
+} // net
+} // vmime
+
+
+#endif // VMIME_BUILTIN_PLATFORM_POSIX
diff --git a/src/net/service.cpp b/src/net/service.cpp
new file mode 100644
index 00000000..9cf59d84
--- /dev/null
+++ b/src/net/service.cpp
@@ -0,0 +1,70 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/service.hpp"
+
+#include "vmime/net/defaultAuthenticator.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+service::service(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+ : m_session(sess), m_auth(auth)
+{
+ if (!auth)
+ {
+ m_auth = vmime::create <defaultAuthenticator>
+ (sess, infos.getPropertyPrefix());
+ }
+}
+
+
+service::~service()
+{
+}
+
+
+ref <const session> service::getSession() const
+{
+ return (m_session);
+}
+
+
+ref <session> service::getSession()
+{
+ return (m_session);
+}
+
+
+ref <const authenticator> service::getAuthenticator() const
+{
+ return (m_auth);
+}
+
+
+ref <authenticator> service::getAuthenticator()
+{
+ return (m_auth);
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/serviceFactory.cpp b/src/net/serviceFactory.cpp
new file mode 100644
index 00000000..43697cc8
--- /dev/null
+++ b/src/net/serviceFactory.cpp
@@ -0,0 +1,124 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/serviceFactory.hpp"
+#include "vmime/net/service.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/config.hpp"
+
+#include "src/net/builtinServices.inl"
+
+
+namespace vmime {
+namespace net {
+
+
+serviceFactory::serviceFactory()
+{
+}
+
+
+serviceFactory::~serviceFactory()
+{
+}
+
+
+serviceFactory* serviceFactory::getInstance()
+{
+ static serviceFactory instance;
+ return (&instance);
+}
+
+
+ref <service> serviceFactory::create
+ (ref <session> sess, const string& protocol, ref <authenticator> auth)
+{
+ return (getServiceByProtocol(protocol)->create(sess, auth));
+}
+
+
+ref <service> serviceFactory::create
+ (ref <session> sess, const utility::url& u, ref <authenticator> auth)
+{
+ ref <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);
+}
+
+
+ref <const serviceFactory::registeredService> serviceFactory::getServiceByProtocol(const string& protocol) const
+{
+ const string name(utility::stringUtils::toLower(protocol));
+
+ for (std::vector <ref <registeredService> >::const_iterator it = m_services.begin() ;
+ it != m_services.end() ; ++it)
+ {
+ if ((*it)->getName() == name)
+ return (*it);
+ }
+
+ throw exceptions::no_service_available(name);
+}
+
+
+const int serviceFactory::getServiceCount() const
+{
+ return (m_services.size());
+}
+
+
+ref <const serviceFactory::registeredService> serviceFactory::getServiceAt(const int pos) const
+{
+ return (m_services[pos]);
+}
+
+
+const std::vector <ref <const serviceFactory::registeredService> > serviceFactory::getServiceList() const
+{
+ std::vector <ref <const registeredService> > res;
+
+ for (std::vector <ref <registeredService> >::const_iterator it = m_services.begin() ;
+ it != m_services.end() ; ++it)
+ {
+ res.push_back(*it);
+ }
+
+ return (res);
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/serviceInfos.cpp b/src/net/serviceInfos.cpp
new file mode 100644
index 00000000..975e01e0
--- /dev/null
+++ b/src/net/serviceInfos.cpp
@@ -0,0 +1,150 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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::SERVER_SOCKETFACTORY
+ ("server.socket-factory", serviceInfos::property::TYPE_STRING, "default");
+
+const serviceInfos::property serviceInfos::property::AUTH_USERNAME
+ ("auth.username", serviceInfos::property::TYPE_STRING);
+
+const serviceInfos::property serviceInfos::property::AUTH_PASSWORD
+ ("auth.password", serviceInfos::property::TYPE_STRING);
+
+const serviceInfos::property serviceInfos::property::TIMEOUT_FACTORY
+ ("timeout.factory", serviceInfos::property::TYPE_STRING);
+
+
+
+// serviceInfos
+
+serviceInfos::serviceInfos()
+{
+}
+
+
+serviceInfos::serviceInfos(const serviceInfos&)
+{
+}
+
+
+serviceInfos& serviceInfos::operator=(const serviceInfos&)
+{
+ return (*this);
+}
+
+
+serviceInfos::~serviceInfos()
+{
+}
+
+
+const bool serviceInfos::hasProperty(ref <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);
+}
+
+
+const serviceInfos::property::Types serviceInfos::property::getType() const
+{
+ return (m_type);
+}
+
+
+const int serviceInfos::property::getFlags() const
+{
+ return (m_flags);
+}
+
+
+} // net
+} // vmime
+
diff --git a/src/net/session.cpp b/src/net/session.cpp
new file mode 100644
index 00000000..2c46bf60
--- /dev/null
+++ b/src/net/session.cpp
@@ -0,0 +1,126 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#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()
+{
+}
+
+
+session::session(const session& sess)
+ : object(), m_props(sess.m_props)
+{
+}
+
+
+session::session(const propertySet& props)
+ : m_props(props)
+{
+}
+
+
+session::~session()
+{
+}
+
+
+ref <transport> session::getTransport(ref <authenticator> auth)
+{
+ return (getTransport(m_props["transport.protocol"], auth));
+}
+
+
+ref <transport> session::getTransport(const string& protocol, ref <authenticator> auth)
+{
+ ref <session> sess = thisRef().dynamicCast <session>();
+ ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
+
+ if (sv->getType() != service::TYPE_TRANSPORT)
+ throw exceptions::no_service_available();
+
+ return sv.staticCast <transport>();
+}
+
+
+ref <transport> session::getTransport(const utility::url& url, ref <authenticator> auth)
+{
+ ref <session> sess = thisRef().dynamicCast <session>();
+ ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);
+
+ if (sv->getType() != service::TYPE_TRANSPORT)
+ throw exceptions::no_service_available();
+
+ return sv.staticCast <transport>();
+}
+
+
+ref <store> session::getStore(ref <authenticator> auth)
+{
+ return (getStore(m_props["store.protocol"], auth));
+}
+
+
+ref <store> session::getStore(const string& protocol, ref <authenticator> auth)
+{
+ ref <session> sess = thisRef().dynamicCast <session>();
+ ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
+
+ if (sv->getType() != service::TYPE_STORE)
+ throw exceptions::no_service_available();
+
+ return sv.staticCast <store>();
+}
+
+
+ref <store> session::getStore(const utility::url& url, ref <authenticator> auth)
+{
+ ref <session> sess = thisRef().dynamicCast <session>();
+ ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);
+
+ if (sv->getType() != service::TYPE_STORE)
+ throw exceptions::no_service_available();
+
+ return sv.staticCast <store>();
+}
+
+
+const propertySet& session::getProperties() const
+{
+ return (m_props);
+}
+
+
+propertySet& session::getProperties()
+{
+ return (m_props);
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/simpleAuthenticator.cpp b/src/net/simpleAuthenticator.cpp
new file mode 100644
index 00000000..1b6db7b4
--- /dev/null
+++ b/src/net/simpleAuthenticator.cpp
@@ -0,0 +1,69 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/simpleAuthenticator.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+simpleAuthenticator::simpleAuthenticator()
+{
+}
+
+
+simpleAuthenticator::simpleAuthenticator(const string& username, const string& password)
+ : m_username(username), m_password(password)
+{
+}
+
+
+const authenticationInfos simpleAuthenticator::getAuthInfos() const
+{
+ return (authenticationInfos(m_username, m_password));
+}
+
+
+const string& simpleAuthenticator::getUsername() const
+{
+ return (m_username);
+}
+
+
+void simpleAuthenticator::setUsername(const string& username)
+{
+ m_username = username;
+}
+
+
+const string& simpleAuthenticator::getPassword() const
+{
+ return (m_password);
+}
+
+
+void simpleAuthenticator::setPassword(const string& password)
+{
+ m_password = password;
+}
+
+
+} // net
+} // vmime
diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp
new file mode 100644
index 00000000..1cbffcf5
--- /dev/null
+++ b/src/net/smtp/SMTPTransport.cpp
@@ -0,0 +1,503 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/smtp/SMTPTransport.hpp"
+
+#include "vmime/exception.hpp"
+#include "vmime/platformDependant.hpp"
+#include "vmime/encoderB64.hpp"
+#include "vmime/mailboxList.hpp"
+
+#include "vmime/net/authHelper.hpp"
+
+#include "vmime/utility/filteredStream.hpp"
+
+
+// Helpers for service properties
+#define GET_PROPERTY(type, prop) \
+ (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+#define HAS_PROPERTY(prop) \
+ (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth)
+ : transport(sess, getInfosInstance(), auth), m_socket(NULL),
+ m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL)
+{
+}
+
+
+SMTPTransport::~SMTPTransport()
+{
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+}
+
+
+const string SMTPTransport::getProtocolName() const
+{
+ return "smtp";
+}
+
+
+void SMTPTransport::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);
+
+ // Create the time-out handler
+ if (HAS_PROPERTY(PROPERTY_TIMEOUT_FACTORY))
+ {
+ timeoutHandlerFactory* tof = platformDependant::getHandler()->
+ getTimeoutHandlerFactory(GET_PROPERTY(string, PROPERTY_TIMEOUT_FACTORY));
+
+ m_timeoutHandler = tof->create();
+ }
+
+ // Create and connect the socket
+ socketFactory* sf = platformDependant::getHandler()->
+ getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
+
+ m_socket = sf->create();
+ m_socket->connect(address, port);
+
+ // Connection
+ //
+ // eg: C: <connection to server>
+ // --- S: 220 smtp.domain.com Service ready
+
+ string response;
+ readResponse(response);
+
+ if (responseCode(response) != 220)
+ {
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(response);
+ }
+
+ // Identification
+ // First, try Extended SMTP (ESMTP)
+ //
+ // eg: C: EHLO thismachine.ourdomain.com
+ // S: 250 OK
+
+ sendRequest("EHLO " + platformDependant::getHandler()->getHostName());
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ {
+ // Next, try "Basic" SMTP
+ //
+ // eg: C: HELO thismachine.ourdomain.com
+ // S: 250 OK
+
+ sendRequest("HELO " + platformDependant::getHandler()->getHostName());
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ {
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(response);
+ }
+
+ m_extendedSMTP = false;
+ }
+ else
+ {
+ m_extendedSMTP = true;
+ }
+
+ // Authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH))
+ {
+ if (!m_extendedSMTP)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("AUTH", "ESMTP not supported.");
+ }
+
+ const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
+ bool authentified = false;
+
+ enum AuthMethods
+ {
+ First = 0,
+ CRAM_MD5 = First,
+ // TODO: more authentication methods...
+ End
+ };
+
+ for (int currentMethod = First ; !authentified ; ++currentMethod)
+ {
+ switch (currentMethod)
+ {
+ case CRAM_MD5:
+ {
+ sendRequest("AUTH CRAM-MD5");
+ readResponse(response);
+
+ if (responseCode(response) == 334)
+ {
+ encoderB64 base64;
+
+ string challengeB64 = responseText(response);
+ string challenge, challengeHex;
+
+ {
+ utility::inputStreamStringAdapter in(challengeB64);
+ utility::outputStreamStringAdapter out(challenge);
+
+ base64.decode(in, out);
+ }
+
+ hmac_md5(challenge, auth.getPassword(), challengeHex);
+
+ string decoded = auth.getUsername() + " " + challengeHex;
+ string encoded;
+
+ {
+ utility::inputStreamStringAdapter in(decoded);
+ utility::outputStreamStringAdapter out(encoded);
+
+ base64.encode(in, out);
+ }
+
+ sendRequest(encoded);
+ readResponse(response);
+
+ if (responseCode(response) == 235)
+ {
+ authentified = true;
+ }
+ else
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(response);
+ }
+ }
+
+ break;
+ }
+ case End:
+ {
+ // All authentication methods have been tried and
+ // the server does not understand any.
+ throw exceptions::authentication_error(response);
+ }
+
+ }
+ }
+ }
+
+ m_authentified = true;
+}
+
+
+const bool SMTPTransport::isConnected() const
+{
+ return (m_socket && m_socket->isConnected() && m_authentified);
+}
+
+
+void SMTPTransport::disconnect()
+{
+ if (!isConnected())
+ throw exceptions::not_connected();
+
+ internalDisconnect();
+}
+
+
+void SMTPTransport::internalDisconnect()
+{
+ sendRequest("QUIT");
+
+ m_socket->disconnect();
+ m_socket = NULL;
+
+ m_timeoutHandler = NULL;
+
+ m_authentified = false;
+ m_extendedSMTP = false;
+}
+
+
+void SMTPTransport::noop()
+{
+ m_socket->send("NOOP");
+
+ string response;
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ throw exceptions::command_error("NOOP", response);
+}
+
+
+void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients,
+ utility::inputStream& is, const utility::stream::size_type size,
+ utility::progressionListener* progress)
+{
+ // If no recipient/expeditor was found, throw an exception
+ if (recipients.isEmpty())
+ throw exceptions::no_recipient();
+ else if (expeditor.isEmpty())
+ throw exceptions::no_expeditor();
+
+ // Emit the "MAIL" command
+ string response;
+
+ sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">");
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("MAIL", response);
+ }
+
+ // Emit a "RCPT TO" command for each recipient
+ for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
+ {
+ const mailbox& mbox = *recipients.getMailboxAt(i);
+
+ sendRequest("RCPT TO: <" + mbox.getEmail() + ">");
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("RCPT TO", response);
+ }
+ }
+
+ // Send the message data
+ sendRequest("DATA");
+ readResponse(response);
+
+ if (responseCode(response) != 354)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("DATA", response);
+ }
+
+ // Stream copy with "\n." to "\n.." transformation
+ utility::outputStreamSocketAdapter sos(*m_socket);
+ utility::dotFilteredOutputStream fos(sos);
+
+ utility::bufferedStreamCopy(is, fos, size, progress);
+
+ // Send end-of-data delimiter
+ m_socket->sendRaw("\r\n.\r\n", 5);
+ readResponse(response);
+
+ if (responseCode(response) != 250)
+ {
+ internalDisconnect();
+ throw exceptions::command_error("DATA", response);
+ }
+}
+
+
+void SMTPTransport::sendRequest(const string& buffer, const bool end)
+{
+ m_socket->send(buffer);
+ if (end) m_socket->send("\r\n");
+}
+
+
+const int SMTPTransport::responseCode(const string& response)
+{
+ int code = 0;
+
+ if (response.length() >= 3)
+ {
+ code = (response[0] - '0') * 100
+ + (response[1] - '0') * 10
+ + (response[2] - '0');
+ }
+
+ return (code);
+}
+
+
+const string SMTPTransport::responseText(const string& response)
+{
+ string text;
+
+ std::istringstream iss(response);
+ std::string line;
+
+ while (std::getline(iss, line))
+ {
+ if (line.length() >= 4)
+ text += line.substr(4);
+ else
+ text += line;
+
+ text += "\n";
+ }
+
+ return (text);
+}
+
+
+void SMTPTransport::readResponse(string& buffer)
+{
+ bool foundTerminator = false;
+
+ buffer.clear();
+
+ 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();
+ }
+
+ // Receive data from the socket
+ string receiveBuffer;
+ m_socket->receive(receiveBuffer);
+
+ if (receiveBuffer.empty()) // buffer is empty
+ {
+ platformDependant::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data: reset the time-out counter
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ // Append the data to the response buffer
+ buffer += receiveBuffer;
+
+ // Check for terminator string (and strip it if present)
+ if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n')
+ {
+ string::size_type p = buffer.length() - 2;
+ bool end = false;
+
+ for ( ; !end ; --p)
+ {
+ if (p == 0 || buffer[p] == '\n')
+ {
+ end = true;
+
+ if (p + 4 < buffer.length())
+ foundTerminator = true;
+ }
+ }
+ }
+ }
+
+ // Remove [CR]LF at the end of the response
+ if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n')
+ {
+ if (buffer[buffer.length() - 2] == '\r')
+ buffer.resize(buffer.length() - 2);
+ else
+ buffer.resize(buffer.length() - 1);
+ }
+}
+
+
+
+// Service infos
+
+SMTPTransport::_infos SMTPTransport::sm_infos;
+
+
+const serviceInfos& SMTPTransport::getInfosInstance()
+{
+ return (sm_infos);
+}
+
+
+const serviceInfos& SMTPTransport::getInfos() const
+{
+ return (sm_infos);
+}
+
+
+const string SMTPTransport::_infos::getPropertyPrefix() const
+{
+ return "transport.smtp.";
+}
+
+
+const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const
+{
+ static props p =
+ {
+ // SMTP-specific options
+ property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"),
+
+ // Common properties
+ property(serviceInfos::property::AUTH_USERNAME),
+ property(serviceInfos::property::AUTH_PASSWORD),
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "25"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return p;
+}
+
+
+const std::vector <serviceInfos::property> SMTPTransport::_infos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // SMTP-specific options
+ list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH);
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return (list);
+}
+
+
+} // smtp
+} // net
+} // vmime
diff --git a/src/net/transport.cpp b/src/net/transport.cpp
new file mode 100644
index 00000000..4f9acbeb
--- /dev/null
+++ b/src/net/transport.cpp
@@ -0,0 +1,116 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "vmime/net/transport.hpp"
+
+#include "vmime/utility/stream.hpp"
+#include "vmime/mailboxList.hpp"
+#include "vmime/message.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+transport::transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+ : service(sess, infos, auth)
+{
+}
+
+
+static void extractMailboxes
+ (mailboxList& recipients, const addressList& list)
+{
+ for (int i = 0 ; i < list.getAddressCount() ; ++i)
+ {
+ ref <mailbox> mbox = list.getAddressAt(i)->clone().dynamicCast <mailbox>();
+
+ if (mbox != NULL)
+ recipients.appendMailbox(mbox);
+ }
+}
+
+
+void transport::send(ref <vmime::message> msg, utility::progressionListener* progress)
+{
+ // Extract expeditor
+ mailbox expeditor;
+
+ try
+ {
+ const mailboxField& from = dynamic_cast <const mailboxField&>
+ (*msg->getHeader()->findField(fields::FROM));
+ expeditor = from.getValue();
+ }
+ catch (exceptions::no_such_field&)
+ {
+ throw exceptions::no_expeditor();
+ }
+
+ // Extract recipients
+ mailboxList recipients;
+
+ try
+ {
+ const addressListField& to = dynamic_cast <const addressListField&>
+ (*msg->getHeader()->findField(fields::TO));
+ extractMailboxes(recipients, to.getValue());
+ }
+ catch (exceptions::no_such_field&) { }
+
+ try
+ {
+ const addressListField& cc = dynamic_cast <const addressListField&>
+ (*msg->getHeader()->findField(fields::CC));
+ extractMailboxes(recipients, cc.getValue());
+ }
+ catch (exceptions::no_such_field&) { }
+
+ try
+ {
+ const addressListField& bcc = dynamic_cast <const addressListField&>
+ (*msg->getHeader()->findField(fields::BCC));
+ extractMailboxes(recipients, bcc.getValue());
+ }
+ catch (exceptions::no_such_field&) { }
+
+ // 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);
+}
+
+
+const transport::Type transport::getType() const
+{
+ return (TYPE_TRANSPORT);
+}
+
+
+} // net
+} // vmime
+
diff --git a/src/platforms/posix/posixHandler.cpp b/src/platforms/posix/posixHandler.cpp
index cd7f35fa..48d96984 100644
--- a/src/platforms/posix/posixHandler.cpp
+++ b/src/platforms/posix/posixHandler.cpp
@@ -166,14 +166,14 @@ const unsigned int posixHandler::getProcessId() const
#if VMIME_HAVE_MESSAGING_FEATURES
-vmime::messaging::socketFactory* posixHandler::getSocketFactory
+vmime::net::socketFactory* posixHandler::getSocketFactory
(const vmime::string& /* name */) const
{
return (m_socketFactory);
}
-vmime::messaging::timeoutHandlerFactory* posixHandler::getTimeoutHandlerFactory
+vmime::net::timeoutHandlerFactory* posixHandler::getTimeoutHandlerFactory
(const vmime::string& /* name */) const
{
// Not used by default
diff --git a/src/platforms/posix/posixSocket.cpp b/src/platforms/posix/posixSocket.cpp
index be346b2a..8a042950 100644
--- a/src/platforms/posix/posixSocket.cpp
+++ b/src/platforms/posix/posixSocket.cpp
@@ -172,7 +172,7 @@ void posixSocket::sendRaw(const char* buffer, const int count)
// posixSocketFactory
//
-ref <vmime::messaging::socket> posixSocketFactory::create()
+ref <vmime::net::socket> posixSocketFactory::create()
{
return vmime::create <posixSocket>();
}
diff --git a/src/platforms/windows/windowsHandler.cpp b/src/platforms/windows/windowsHandler.cpp
index d6f28037..19788642 100644
--- a/src/platforms/windows/windowsHandler.cpp
+++ b/src/platforms/windows/windowsHandler.cpp
@@ -233,14 +233,14 @@ const unsigned int windowsHandler::getProcessId() const
#if VMIME_HAVE_MESSAGING_FEATURES
-vmime::messaging::socketFactory* windowsHandler::getSocketFactory
+vmime::net::socketFactory* windowsHandler::getSocketFactory
(const vmime::string& /* name */) const
{
return (m_socketFactory);
}
-vmime::messaging::timeoutHandlerFactory* windowsHandler::getTimeoutHandlerFactory
+vmime::net::timeoutHandlerFactory* windowsHandler::getTimeoutHandlerFactory
(const vmime::string& /* name */) const
{
// Not used by default
diff --git a/src/platforms/windows/windowsSocket.cpp b/src/platforms/windows/windowsSocket.cpp
index b325eedd..bedfb07c 100644
--- a/src/platforms/windows/windowsSocket.cpp
+++ b/src/platforms/windows/windowsSocket.cpp
@@ -167,7 +167,7 @@ void windowsSocket::sendRaw(const char* buffer, const int count)
// posixSocketFactory
//
-ref <vmime::messaging::socket> windowsSocketFactory::create()
+ref <vmime::net::socket> windowsSocketFactory::create()
{
return vmime::create <windowsSocket>();
}
diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp
index 799c6c32..49ebcf66 100644
--- a/src/utility/stream.cpp
+++ b/src/utility/stream.cpp
@@ -24,7 +24,7 @@
#include <iterator> // for std::back_inserter
#if VMIME_HAVE_MESSAGING_FEATURES
- #include "vmime/messaging/socket.hpp"
+ #include "vmime/net/socket.hpp"
#endif
@@ -331,7 +331,7 @@ const stream::size_type inputStreamPointerAdapter::skip(const size_type count)
// outputStreamSocketAdapter
-outputStreamSocketAdapter::outputStreamSocketAdapter(messaging::socket& sok)
+outputStreamSocketAdapter::outputStreamSocketAdapter(net::socket& sok)
: m_socket(sok)
{
}
@@ -346,7 +346,7 @@ void outputStreamSocketAdapter::write
// inputStreamSocketAdapter
-inputStreamSocketAdapter::inputStreamSocketAdapter(messaging::socket& sok)
+inputStreamSocketAdapter::inputStreamSocketAdapter(net::socket& sok)
: m_socket(sok)
{
}
diff --git a/vmime/exception.hpp b/vmime/exception.hpp
index 3f4c98ea..c09e8919 100644
--- a/vmime/exception.hpp
+++ b/vmime/exception.hpp
@@ -338,26 +338,32 @@ public:
#if VMIME_HAVE_MESSAGING_FEATURES
-/** Base class for exceptions thrown by the messaging module.
+/** Base class for exceptions thrown by the networking module.
*/
-class messaging_exception : public vmime::exception
+class net_exception : public vmime::exception
{
public:
- messaging_exception(const string& what, const exception& other = NO_EXCEPTION);
- ~messaging_exception() throw();
+ net_exception(const string& what, const exception& other = NO_EXCEPTION);
+ ~net_exception() throw();
exception* clone() const;
const char* name() const throw();
};
+/** Alias for 'net_exception' (compatibility with version <= 0.7.1);
+ * this is deprecated.
+ */
+typedef net_exception messaging_exception;
+
+
/** Error while connecting to the server: this may be a DNS resolution error
* or a connection error (for example, time-out while connecting).
*/
-class connection_error : public messaging_exception
+class connection_error : public net_exception
{
public:
@@ -372,7 +378,7 @@ public:
/** Server did not initiated the connection correctly.
*/
-class connection_greeting_error : public messaging_exception
+class connection_greeting_error : public net_exception
{
public:
@@ -394,7 +400,7 @@ private:
* or password, or wrong authentication method).
*/
-class authentication_error : public messaging_exception
+class authentication_error : public net_exception
{
public:
@@ -415,7 +421,7 @@ private:
/** Option not supported.
*/
-class unsupported_option : public messaging_exception
+class unsupported_option : public net_exception
{
public:
@@ -430,7 +436,7 @@ public:
/** No service available for this protocol.
*/
-class no_service_available : public messaging_exception
+class no_service_available : public net_exception
{
public:
@@ -446,7 +452,7 @@ public:
* operation (for example, you try to close a folder which is not open).
*/
-class illegal_state : public messaging_exception
+class illegal_state : public net_exception
{
public:
@@ -461,7 +467,7 @@ public:
/** Folder not found (does not exist).
*/
-class folder_not_found : public messaging_exception
+class folder_not_found : public net_exception
{
public:
@@ -476,7 +482,7 @@ public:
/** Message not found (does not exist).
*/
-class message_not_found : public messaging_exception
+class message_not_found : public net_exception
{
public:
@@ -491,7 +497,7 @@ public:
/** Operation not supported by the underlying protocol.
*/
-class operation_not_supported : public messaging_exception
+class operation_not_supported : public net_exception
{
public:
@@ -506,7 +512,7 @@ public:
/** The operation timed out (time-out delay is elapsed).
*/
-class operation_timed_out : public messaging_exception
+class operation_timed_out : public net_exception
{
public:
@@ -521,7 +527,7 @@ public:
/** The operation has been cancelled.
*/
-class operation_cancelled : public messaging_exception
+class operation_cancelled : public net_exception
{
public:
@@ -537,7 +543,7 @@ public:
* the requested object.
*/
-class unfetched_object : public messaging_exception
+class unfetched_object : public net_exception
{
public:
@@ -552,7 +558,7 @@ public:
/** The service is not currently connected.
*/
-class not_connected : public messaging_exception
+class not_connected : public net_exception
{
public:
@@ -567,7 +573,7 @@ public:
/** The service is already connected (must disconnect before).
*/
-class already_connected : public messaging_exception
+class already_connected : public net_exception
{
public:
@@ -582,7 +588,7 @@ public:
/** Illegal operation: cannot run this operation on the object.
*/
-class illegal_operation : public messaging_exception
+class illegal_operation : public net_exception
{
public:
@@ -597,7 +603,7 @@ public:
/** Command error: operation failed (this is specific to the underlying protocol).
*/
-class command_error : public messaging_exception
+class command_error : public net_exception
{
public:
@@ -631,7 +637,7 @@ private:
/** The server returned an invalid response.
*/
-class invalid_response : public messaging_exception
+class invalid_response : public net_exception
{
public:
@@ -665,7 +671,7 @@ private:
/** Partial fetch is not supported by the underlying protocol.
*/
-class partial_fetch_not_supported : public messaging_exception
+class partial_fetch_not_supported : public net_exception
{
public:
@@ -680,7 +686,7 @@ public:
/** The URL is malformed.
*/
-class malformed_url : public messaging_exception
+class malformed_url : public net_exception
{
public:
@@ -695,7 +701,7 @@ public:
/** Folder name is invalid.
*/
-class invalid_folder_name : public messaging_exception
+class invalid_folder_name : public net_exception
{
public:
diff --git a/vmime/net/authHelper.hpp b/vmime/net/authHelper.hpp
new file mode 100644
index 00000000..4ceffc92
--- /dev/null
+++ b/vmime/net/authHelper.hpp
@@ -0,0 +1,38 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_AUTHHELPER_HPP_INCLUDED
+#define VMIME_NET_AUTHHELPER_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+void hmac_md5(const string& text, const string& key, string& hexDigest);
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_AUTHHELPER_HPP_INCLUDED
diff --git a/vmime/net/authenticationInfos.hpp b/vmime/net/authenticationInfos.hpp
new file mode 100644
index 00000000..2bc394fd
--- /dev/null
+++ b/vmime/net/authenticationInfos.hpp
@@ -0,0 +1,64 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
+#define VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** This class encapsulates user credentials.
+ */
+
+class authenticationInfos : public object
+{
+public:
+
+ authenticationInfos(const string& username, const string& password);
+ authenticationInfos(const authenticationInfos& infos);
+
+ /** Return the user account name.
+ *
+ * @return account name
+ */
+ const string& getUsername() const;
+
+ /** Return the user account password.
+ *
+ * @return account password
+ */
+ const string& getPassword() const;
+
+private:
+
+ string m_username;
+ string m_password;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
diff --git a/vmime/net/authenticator.hpp b/vmime/net/authenticator.hpp
new file mode 100644
index 00000000..7e58a70b
--- /dev/null
+++ b/vmime/net/authenticator.hpp
@@ -0,0 +1,54 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_AUTHENTICATOR_HPP_INCLUDED
+#define VMIME_NET_AUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+#include "vmime/net/authenticationInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** This class is used to obtain user credentials.
+ */
+
+class authenticator : public object
+{
+public:
+
+ virtual ~authenticator();
+
+ /** Called when the service needs to retrieve user credentials.
+ * It should return the user account name and password.
+ *
+ * @return user credentials (user name and password)
+ */
+ virtual const authenticationInfos requestAuthInfos() const = 0;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_AUTHENTICATOR_HPP_INCLUDED
diff --git a/vmime/net/defaultAuthenticator.hpp b/vmime/net/defaultAuthenticator.hpp
new file mode 100644
index 00000000..58dccfca
--- /dev/null
+++ b/vmime/net/defaultAuthenticator.hpp
@@ -0,0 +1,60 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+#define VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/net/authenticator.hpp"
+#include "vmime/propertySet.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class session;
+
+
+/** Default implementation for authenticator. It simply returns
+ * the credentials set in the session properties (named 'username'
+ * and 'password'). This is the default implementation used if
+ * you do not write your own authenticator object.
+ */
+
+class defaultAuthenticator : public authenticator
+{
+public:
+
+ defaultAuthenticator(weak_ref <session> session, const string& prefix);
+
+private:
+
+ weak_ref <session> m_session;
+ const string m_prefix;
+
+ const authenticationInfos requestAuthInfos() const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
diff --git a/vmime/net/events.hpp b/vmime/net/events.hpp
new file mode 100644
index 00000000..6c3c91fc
--- /dev/null
+++ b/vmime/net/events.hpp
@@ -0,0 +1,229 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_EVENTS_HPP_INCLUDED
+#define VMIME_NET_EVENTS_HPP_INCLUDED
+
+
+#include <vector>
+
+#include "vmime/utility/path.hpp"
+
+
+namespace vmime {
+namespace net {
+
+class folder;
+
+namespace events {
+
+
+/** Event about the message count in a folder.
+ */
+
+class messageCountEvent
+{
+public:
+
+ enum Types
+ {
+ TYPE_ADDED, /**< New messages have been added. */
+ TYPE_REMOVED /**< Messages have been expunged (renumbering). */
+ };
+
+
+ messageCountEvent(ref <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
+ */
+ ref <const folder> getFolder() const;
+
+ /** Return the event type.
+ *
+ * @return event type (see messageCountEvent::Types)
+ */
+ const 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;
+
+private:
+
+ ref <folder> m_folder;
+ const Types m_type;
+ std::vector <int> m_nums;
+};
+
+
+/** Listener for events about the message count in a folder.
+ */
+
+class messageCountListener
+{
+protected:
+
+ virtual ~messageCountListener() { }
+
+public:
+
+ virtual void messagesAdded(const messageCountEvent& event) = 0;
+ virtual void messagesRemoved(const messageCountEvent& event) = 0;
+};
+
+
+/** Event occuring on a message.
+ */
+
+class messageChangedEvent
+{
+public:
+
+ enum Types
+ {
+ TYPE_FLAGS // flags changed
+ };
+
+
+ messageChangedEvent(ref <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
+ */
+ ref <const folder> getFolder() const;
+
+ /** Return the event type.
+ *
+ * @return event type (see messageChangedEvent::Types)
+ */
+ const 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;
+
+private:
+
+ ref <folder> m_folder;
+ const Types m_type;
+ std::vector <int> m_nums;
+};
+
+
+/** Listener for events occuring on a message.
+ */
+
+class messageChangedListener
+{
+protected:
+
+ virtual ~messageChangedListener() { }
+
+public:
+
+ virtual void messageChanged(const messageChangedEvent& event) = 0;
+};
+
+
+/** Event occuring on a folder.
+ */
+
+class folderEvent
+{
+public:
+
+ enum Types
+ {
+ TYPE_CREATED, /**< A folder was created. */
+ TYPE_DELETED, /**< A folder was deleted. */
+ TYPE_RENAMED /**< A folder was renamed. */
+ };
+
+
+ folderEvent(ref <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
+ */
+ ref <const folder> getFolder() const;
+
+ /** Return the event type.
+ *
+ * @return event type (see folderEvent::Types)
+ */
+ const Types getType() const;
+
+ /** Dispatch the event to the specified listener.
+ *
+ * @param listener listener to notify
+ */
+ void dispatch(class folderListener* listener) const;
+
+private:
+
+ ref <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 folderListener
+{
+protected:
+
+ virtual ~folderListener() { }
+
+public:
+
+ virtual void folderCreated(const folderEvent& event) = 0;
+ virtual void folderRenamed(const folderEvent& event) = 0;
+ virtual void folderDeleted(const folderEvent& event) = 0;
+};
+
+
+} // events
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_EVENTS_HPP_INCLUDED
diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp
new file mode 100644
index 00000000..68e0173b
--- /dev/null
+++ b/vmime/net/folder.hpp
@@ -0,0 +1,379 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_FOLDER_HPP_INCLUDED
+#define VMIME_NET_FOLDER_HPP_INCLUDED
+
+
+#include <vector>
+
+#include "vmime/types.hpp"
+#include "vmime/dateTime.hpp"
+
+#include "vmime/message.hpp"
+#include "vmime/net/message.hpp"
+#include "vmime/net/events.hpp"
+
+#include "vmime/utility/path.hpp"
+#include "vmime/utility/stream.hpp"
+#include "vmime/utility/progressionListener.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class store;
+
+
+/** Abstract representation of a folder in a message store.
+ */
+
+class 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 const int getType() = 0;
+
+ /** Return the flags of this folder.
+ *
+ * @return folder flags (see folder::Flags)
+ */
+ virtual const int getFlags() = 0;
+
+ /** Return the mode in which the folder has been open.
+ *
+ * @return folder opening mode (see folder::Modes)
+ */
+ virtual const 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.
+ */
+ virtual void open(const int mode, bool failIfModeIsNotAvailable = false) = 0;
+
+ /** Close this folder.
+ *
+ * @param expunge if set to true, deleted messages are expunged
+ */
+ virtual void close(const bool expunge) = 0;
+
+ /** Create this folder.
+ *
+ * @param type folder type (see folder::Types)
+ */
+ virtual void create(const int type) = 0;
+
+ /** Test whether this folder exists.
+ *
+ * @return true if the folder exists, false otherwise
+ */
+ virtual const bool exists() = 0;
+
+ /** Test whether this folder is open.
+ *
+ * @return true if the folder is open, false otherwise
+ */
+ virtual const bool isOpen() const = 0;
+
+ /** Get a new reference to a message in this folder.
+ *
+ * @param num message sequence number
+ * @return a new object referencing the specified message
+ */
+ virtual ref <message> getMessage(const int num) = 0;
+
+ /** Get new references to messages in this folder.
+ *
+ * @param from sequence number of the first message to get
+ * @param to sequence number of the last message to get
+ * @return new objects referencing the specified messages
+ */
+ virtual std::vector <ref <message> > getMessages(const int from = 1, const int to = -1) = 0;
+
+ /** Get new references to messages in this folder.
+ *
+ * @param nums sequence numbers of the messages to delete
+ * @return new objects referencing the specified messages
+ */
+ virtual std::vector <ref <message> > getMessages(const std::vector <int>& nums) = 0;
+
+ /** Return the number of messages in this folder.
+ *
+ * @return number of messages in the folder
+ */
+ virtual const 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
+ */
+ virtual ref <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
+ */
+ virtual std::vector <ref <folder> > getFolders(const bool recursive = false) = 0;
+
+ /** Rename (move) this folder to another location.
+ *
+ * @param newPath new path of the folder
+ */
+ virtual void rename(const folder::path& newPath) = 0;
+
+ /** Remove a message in this folder.
+ *
+ * @param num sequence number of the message to delete
+ */
+ virtual void deleteMessage(const int num) = 0;
+
+ /** Remove one or more messages from this folder.
+ *
+ * @param from sequence number of the first message to delete
+ * @param to sequence number of the last message to delete
+ */
+ virtual void deleteMessages(const int from = 1, const int to = -1) = 0;
+
+ /** Remove one or more messages from this folder.
+ *
+ * @param nums sequence numbers of the messages to delete
+ */
+ virtual void deleteMessages(const std::vector <int>& nums) = 0;
+
+ /** Change the flags for one or more messages in this folder.
+ *
+ * @param from sequence number of the first message to modify
+ * @param to sequence number of the last message to modify
+ * @param flags set of flags (see message::Flags)
+ * @param mode indicate how to treat old and new flags (see message::FlagsModes)
+ */
+ virtual void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET) = 0;
+
+ /** Change the flags for one or more messages in this folder.
+ *
+ * @param nums sequence numbers of the messages to modify
+ * @param flags set of flags (see message::Flags)
+ * @param mode indicate how to treat old and new flags (see message::FlagsModes)
+ */
+ virtual void setMessageFlags(const std::vector <int>& nums, 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 progression listener, or NULL if not used
+ */
+ virtual void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* 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 progression listener, or NULL if not used
+ */
+ virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL) = 0;
+
+ /** Copy a message from this folder to another folder.
+ *
+ * @param dest destination folder path
+ * @param num sequence number of the message to copy
+ */
+ virtual void copyMessage(const folder::path& dest, const int num) = 0;
+
+ /** Copy messages from this folder to another folder.
+ *
+ * @param dest destination folder path
+ * @param from sequence number of the first message to copy
+ * @param to sequence number of the last message to copy
+ */
+ virtual void copyMessages(const folder::path& dest, const int from = 1, const int to = -1) = 0;
+
+ /** Copy messages from this folder to another folder.
+ *
+ * @param dest destination folder path
+ * @param nums sequence numbers of the messages to copy
+ */
+ virtual void copyMessages(const folder::path& dest, const std::vector <int>& nums) = 0;
+
+ /** Request folder status without opening it.
+ *
+ * @param count will receive the number of messages in the folder
+ * @param unseen will receive the number of unseen messages in the folder
+ */
+ virtual void status(int& count, int& unseen) = 0;
+
+ /** Expunge deleted messages.
+ */
+ virtual void expunge() = 0;
+
+ /** Return a new folder object referencing the parent folder of this folder.
+ *
+ * @return parent folder object
+ */
+ virtual ref <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 weak_ref <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 weak_ref <store> getStore() = 0;
+
+ /** Fetchable objects.
+ */
+ enum FetchOptions
+ {
+ FETCH_ENVELOPE = (1 << 0), /**< Fetch sender, recipients, date, subject. */
+ FETCH_STRUCTURE = (1 << 1), /**< Fetch structure (body parts). */
+ FETCH_CONTENT_INFO = (1 << 2), /**< Fetch top-level content type. */
+ FETCH_FLAGS = (1 << 3), /**< Fetch message flags. */
+ FETCH_SIZE = (1 << 4), /**< Fetch message size (exact or estimated). */
+ FETCH_FULL_HEADER = (1 << 5), /**< Fetch full RFC-[2]822 header. */
+ FETCH_UID = (1 << 6), /**< Fetch unique identifier (protocol specific). */
+ FETCH_IMPORTANCE = (1 << 7), /**< Fetch header fields suitable for use with misc::importanceHelper. */
+
+ FETCH_CUSTOM = (1 << 16) /**< Reserved for future use. */
+ };
+
+ /** Fetch objects for the specified messages.
+ *
+ * @param msg list of message sequence numbers
+ * @param options objects to fetch (combination of folder::FetchOptions flags)
+ * @param progress progression listener, or NULL if not used
+ */
+ virtual void fetchMessages(std::vector <ref <message> >& msg, const int options, utility::progressionListener* progress = NULL) = 0;
+
+ /** Fetch objects for the specified message.
+ *
+ * @param msg the message
+ * @param options objects to fetch (combination of folder::FetchOptions flags)
+ */
+ virtual void fetchMessage(ref <message> msg, const int options) = 0;
+
+ /** Return the list of fetchable objects supported by
+ * the underlying protocol (see folder::FetchOptions).
+ *
+ * @return list of supported fetchable objects
+ */
+ virtual const int getFetchCapabilities() const = 0;
+
+ // Event listeners
+ void addMessageChangedListener(events::messageChangedListener* l);
+ void removeMessageChangedListener(events::messageChangedListener* l);
+
+ void addMessageCountListener(events::messageCountListener* l);
+ void removeMessageCountListener(events::messageCountListener* l);
+
+ void addFolderListener(events::folderListener* l);
+ void removeFolderListener(events::folderListener* l);
+
+protected:
+
+ void notifyMessageChanged(const events::messageChangedEvent& event);
+ void notifyMessageCount(const events::messageCountEvent& event);
+ void notifyFolder(const events::folderEvent& event);
+
+private:
+
+ std::list <events::messageChangedListener*> m_messageChangedListeners;
+ std::list <events::messageCountListener*> m_messageCountListeners;
+ std::list <events::folderListener*> m_folderListeners;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_FOLDER_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPConnection.hpp b/vmime/net/imap/IMAPConnection.hpp
new file mode 100644
index 00000000..542f4869
--- /dev/null
+++ b/vmime/net/imap/IMAPConnection.hpp
@@ -0,0 +1,116 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/authenticator.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+#include "vmime/net/session.hpp"
+
+#include "vmime/net/imap/IMAPParser.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class IMAPTag;
+class IMAPStore;
+
+
+class IMAPConnection : public object
+{
+public:
+
+ IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth);
+ ~IMAPConnection();
+
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+
+ enum ProtocolStates
+ {
+ STATE_NONE,
+ STATE_NON_AUTHENTICATED,
+ STATE_AUTHENTICATED,
+ STATE_SELECTED,
+ STATE_LOGOUT
+ };
+
+ const ProtocolStates state() const;
+ void setState(const ProtocolStates state);
+
+
+ const char hierarchySeparator() const;
+
+
+ void send(bool tag, const string& what, bool end);
+ void sendRaw(const char* buffer, const int count);
+
+ IMAPParser::response* readResponse(IMAPParser::literalHandler* lh = NULL);
+
+
+ ref <const IMAPTag> getTag() const;
+ ref <const IMAPParser> getParser() const;
+
+ weak_ref <const IMAPStore> getStore() const;
+ weak_ref <IMAPStore> getStore();
+
+ ref <session> getSession();
+
+private:
+
+ weak_ref <IMAPStore> m_store;
+
+ ref <authenticator> m_auth;
+
+ ref <socket> m_socket;
+
+ ref <IMAPParser> m_parser;
+
+ ref <IMAPTag> m_tag;
+
+ char m_hierarchySeparator;
+
+ ProtocolStates m_state;
+
+ ref <timeoutHandler> m_timeoutHandler;
+
+
+ void internalDisconnect();
+
+ void initHierarchySeparator();
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPCONNECTION_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp
new file mode 100644
index 00000000..880c9a6e
--- /dev/null
+++ b/vmime/net/imap/IMAPFolder.hpp
@@ -0,0 +1,158 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED
+
+
+#include <vector>
+#include <map>
+
+#include "vmime/types.hpp"
+
+#include "vmime/net/folder.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class IMAPStore;
+class IMAPMessage;
+class IMAPConnection;
+
+
+/** IMAP folder implementation.
+ */
+
+class IMAPFolder : public folder
+{
+private:
+
+ friend class IMAPStore;
+ friend class IMAPMessage;
+ friend class vmime::creator; // vmime::create <IMAPFolder>
+
+
+ IMAPFolder(const folder::path& path, IMAPStore* store, const int type = TYPE_UNDEFINED, const int flags = FLAG_UNDEFINED);
+ IMAPFolder(const IMAPFolder&) : folder() { }
+
+ ~IMAPFolder();
+
+public:
+
+ const int getMode() const;
+
+ const int getType();
+
+ const 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);
+
+ const bool exists();
+
+ const bool isOpen() const;
+
+ ref <message> getMessage(const int num);
+ std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
+ std::vector <ref <message> > getMessages(const std::vector <int>& nums);
+ const int getMessageCount();
+
+ ref <folder> getFolder(const folder::path::component& name);
+ std::vector <ref <folder> > getFolders(const bool recursive = false);
+
+ void rename(const folder::path& newPath);
+
+ void deleteMessage(const int num);
+ void deleteMessages(const int from = 1, const int to = -1);
+ void deleteMessages(const std::vector <int>& nums);
+
+ void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+
+ void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+
+ void copyMessage(const folder::path& dest, const int num);
+ void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
+ void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+
+ void status(int& count, int& unseen);
+
+ void expunge();
+
+ ref <folder> getParent();
+
+ weak_ref <const store> getStore() const;
+ weak_ref <store> getStore();
+
+
+ void fetchMessages(std::vector <ref <message> >& msg, const int options, utility::progressionListener* progress = NULL);
+ void fetchMessage(ref <message> msg, const int options);
+
+ const int getFetchCapabilities() const;
+
+private:
+
+ void registerMessage(IMAPMessage* msg);
+ void unregisterMessage(IMAPMessage* msg);
+
+ void onStoreDisconnected();
+
+ void onClose();
+
+ const int testExistAndGetType();
+
+ void setMessageFlags(const string& set, const int flags, const int mode);
+
+ void copyMessages(const string& set, const folder::path& dest);
+
+
+ IMAPStore* m_store;
+ ref <IMAPConnection> m_connection;
+
+ folder::path m_path;
+ folder::path::component m_name;
+
+ int m_mode;
+ bool m_open;
+
+ int m_type;
+ int m_flags;
+
+ int m_messageCount;
+
+ int m_uidValidity;
+
+ std::vector <IMAPMessage*> m_messages;
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPFOLDER_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp
new file mode 100644
index 00000000..bac54219
--- /dev/null
+++ b/vmime/net/imap/IMAPMessage.hpp
@@ -0,0 +1,111 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED
+
+
+#include "vmime/net/message.hpp"
+#include "vmime/net/folder.hpp"
+
+#include "vmime/mailboxList.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class IMAPFolder;
+
+
+/** IMAP message implementation.
+ */
+
+class IMAPMessage : public message
+{
+private:
+
+ friend class IMAPFolder;
+ friend class vmime::creator; // vmime::create <IMAPMessage>
+
+ IMAPMessage(IMAPFolder* folder, const int num);
+ IMAPMessage(const IMAPMessage&) : message() { }
+
+ ~IMAPMessage();
+
+public:
+
+ const int getNumber() const;
+
+ const uid getUniqueId() const;
+
+ const int getSize() const;
+
+ const bool isExpunged() const;
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ ref <const header> getHeader() const;
+
+ const int getFlags() const;
+ void setFlags(const int flags, const int mode = FLAG_MODE_SET);
+
+ void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+ void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+
+ void fetchPartHeader(part& p);
+
+private:
+
+ void fetch(IMAPFolder* folder, const int options);
+
+ void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt);
+
+ void extract(const part* p, utility::outputStream& os, utility::progressionListener* progress, const int start, const int length, const bool headerOnly, const bool peek) const;
+
+
+ void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest);
+
+
+ ref <header> getOrCreateHeader();
+
+
+ void onFolderClosed();
+
+ IMAPFolder* m_folder;
+
+ int m_num;
+ int m_size;
+ int m_flags;
+ bool m_expunged;
+ uid m_uid;
+
+ ref <header> m_header;
+ ref <structure> m_structure;
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPMESSAGE_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp
new file mode 100644
index 00000000..090e6ffe
--- /dev/null
+++ b/vmime/net/imap/IMAPParser.hpp
@@ -0,0 +1,5082 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED
+
+
+#include "vmime/base.hpp"
+#include "vmime/dateTime.hpp"
+#include "vmime/charset.hpp"
+#include "vmime/exception.hpp"
+
+#include "vmime/utility/smartPtr.hpp"
+#include "vmime/utility/stringUtils.hpp"
+#include "vmime/utility/progressionListener.hpp"
+
+#include "vmime/encoderB64.hpp"
+#include "vmime/encoderQP.hpp"
+
+#include "vmime/platformDependant.hpp"
+
+#include "vmime/net/timeoutHandler.hpp"
+#include "vmime/net/socket.hpp"
+
+#include "vmime/net/imap/IMAPTag.hpp"
+
+#include <vector>
+#include <stdexcept>
+
+
+//#define DEBUG_RESPONSE 1
+
+
+#if DEBUG_RESPONSE
+# include <iostream>
+#endif
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+#if DEBUG_RESPONSE
+ static string DEBUG_RESPONSE_level;
+ static std::vector <string> DEBUG_RESPONSE_components;
+
+# define DEBUG_ENTER_COMPONENT(x) \
+ DEBUG_RESPONSE_components.push_back(x); \
+ std::cout << DEBUG_RESPONSE_level \
+ << "(" << DEBUG_RESPONSE_level.length() << ") " \
+ << (x) << std::endl;
+# 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 IMAPParser : public object
+{
+public:
+
+ IMAPParser(weak_ref <IMAPTag> tag, weak_ref <socket> sok, weak_ref <timeoutHandler> _timeoutHandler)
+ : m_tag(tag), m_socket(sok), m_progress(NULL),
+ m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler)
+ {
+ }
+
+
+ weak_ref <const IMAPTag> tag() const
+ {
+ return (m_tag);
+ }
+
+
+ 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);
+ }
+
+
+
+ //
+ // literalHandler : literal content handler
+ //
+
+ class component;
+
+ class literalHandler
+ {
+ public:
+
+ virtual ~literalHandler() { }
+
+
+ // Abstract target class
+ class target
+ {
+ protected:
+
+ target(utility::progressionListener* progress) : m_progress(progress) {}
+ target(const target&) {}
+
+ public:
+
+ virtual ~target() { }
+
+
+ utility::progressionListener* progressionListener() { return (m_progress); }
+
+ virtual void putData(const string& chunk) = 0;
+
+ private:
+
+ utility::progressionListener* m_progress;
+ };
+
+
+ // Target: put in a string
+ class targetString : public target
+ {
+ public:
+
+ targetString(utility::progressionListener* 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::progressionListener* 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, string::size_type* currentPos) = 0;
+
+
+ const string makeResponseLine(const string& comp, const string& line,
+ const string::size_type 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);
+ }
+ };
+
+
+
+ //
+ // Parse one character
+ //
+
+ template <char C>
+ class one_char : public component
+ {
+ public:
+
+ void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'");
+
+ const string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("SPACE");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("CRLF");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("tag");
+
+ string::size_type 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.tag())))
+ {
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("number");
+
+ string::size_type 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 int m_value;
+
+ public:
+
+ const unsigned int 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("text");
+
+ string::size_type pos = *currentPos;
+ string::size_type len = 0;
+
+ if (m_allow8bits)
+ {
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("quoted_char");
+
+ string::size_type pos = *currentPos;
+
+ const unsigned char c = (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:
+
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("quoted_text");
+
+ string::size_type pos = *currentPos;
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("NIL");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("string");
+
+ string::size_type 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))
+ {
+ utility::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 string::size_type length = text->value().length();
+ utility::progressionListener* progress = target->progressionListener();
+
+ 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 string::size_type 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->progressionListener();
+ 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); }
+ };
+
+
+ //
+ // 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("astring");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("atom");
+
+ string::size_type pos = *currentPos;
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT(string("special_atom(") + m_string + ")");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("text_mime2");
+
+ string::size_type 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& e)
+ {
+ delete (theCharset);
+ delete (theEncoding);
+ delete (theText);
+
+ throw;
+ }
+
+ m_charset = theCharset->value();
+ delete (theCharset);
+
+ // Decode text
+ encoder* theEncoder = NULL;
+
+ if (theEncoding->value()[0] == 'q' || theEncoding->value()[0] == 'Q')
+ {
+ // Quoted-printable
+ theEncoder = new encoderQP;
+ theEncoder->getProperties()["rfc2047"] = true;
+ }
+ else if (theEncoding->value()[0] == 'b' || theEncoding->value()[0] == 'B')
+ {
+ // Base64
+ theEncoder = new encoderB64;
+ }
+
+ 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); }
+ };
+
+
+ //
+ // 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_flag_keyword(NULL)
+ {
+ }
+
+ ~flag()
+ {
+ delete (m_flag_keyword);
+ }
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("flag_keyword");
+
+ string::size_type 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_flag_keyword = parser.get <atom>(line, &pos);
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ UNKNOWN,
+ ANSWERED,
+ FLAGGED,
+ DELETED,
+ SEEN,
+ DRAFT,
+ STAR // * = custom flags allowed
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ IMAPParser::atom* m_flag_keyword;
+
+ public:
+
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("flag_list");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox");
+
+ string::size_type 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:
+
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_flag");
+
+ string::size_type pos = *currentPos;
+
+ parser.check <one_char <'\\'> >(line, &pos);
+
+ 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;
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ UNKNOWN,
+ MARKED,
+ NOINFERIORS,
+ NOSELECT,
+ UNMARKED
+ };
+
+ private:
+
+ Type m_type;
+ string m_name;
+
+ public:
+
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_flag_list");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_list");
+
+ string::size_type 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); }
+ const char quoted_char() const { return (m_quoted_char); }
+ };
+
+
+ //
+ // resp_text_code ::= "ALERT" / "PARSE" /
+ // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
+ // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
+ // "UIDVALIDITY" SPACE nz_number /
+ // "UNSEEN" SPACE nz_number /
+ // atom [SPACE 1*<any TEXT_CHAR except "]">]
+
+ class resp_text_code : public component
+ {
+ public:
+
+ resp_text_code()
+ : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), m_text(NULL)
+ {
+ }
+
+ ~resp_text_code()
+ {
+ delete (m_nz_number);
+ delete (m_atom);
+ delete (m_flag_list);
+ delete (m_text);
+ }
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_text_code");
+
+ string::size_type 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;
+ }
+ // "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);
+ }
+ // "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);
+ }
+ // 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
+ {
+ ALERT,
+ PARSE,
+ PERMANENTFLAGS,
+ READ_ONLY,
+ READ_WRITE,
+ TRYCREATE,
+ UIDVALIDITY,
+ 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;
+
+ public:
+
+ const 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); }
+ };
+
+
+ //
+ // 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_text");
+
+ string::size_type 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);
+ }
+
+ 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);
+
+ m_text = text2->value();
+ delete (text2);
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("continue_req");
+
+ string::size_type pos = *currentPos;
+
+ parser.check <one_char <'+'> >(line, &pos);
+ 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); }
+ };
+
+
+ //
+ // auth_type ::= atom
+ // ;; Defined by [IMAP-AUTH]
+ //
+
+ class auth_type : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, string::size_type* 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:
+
+ const Type type() const { return (m_type); }
+ const string name() const { return (m_name); }
+ };
+
+
+ //
+ // status_att ::= "MESSAGES" / "RECENT" / "UIDNEXT" /
+ // "UIDVALIDITY" / "UNSEEN"
+ //
+
+ class status_att : public component
+ {
+ public:
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("status_att");
+
+ string::size_type pos = *currentPos;
+
+ 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;
+ }
+
+ *currentPos = pos;
+ }
+
+
+ enum Type
+ {
+ MESSAGES,
+ RECENT,
+ UIDNEXT,
+ UIDVALIDITY,
+ UNSEEN
+ };
+
+ private:
+
+ Type m_type;
+
+ public:
+
+ const Type type() const { return (m_type); }
+ };
+
+
+ //
+ // 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("capability");
+
+ string::size_type 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] == '='))
+ {
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("capability_data");
+
+ string::size_type pos = *currentPos;
+
+ parser.checkWithArg <special_atom>(line, &pos, "capability");
+ parser.check <SPACE>(line, &pos);
+
+ bool IMAP4rev1 = false;
+
+ for (bool end = false ; !end && !IMAP4rev1 ; )
+ {
+ if (parser.checkWithArg <special_atom>(line, &pos, "imap4rev1", true))
+ {
+ IMAP4rev1 = true;
+ }
+ else
+ {
+ capability* cap = parser.get <capability>(line, &pos);
+ end = (cap == NULL);
+
+ if (cap)
+ {
+ m_capabilities.push_back(cap);
+ }
+ }
+
+ parser.check <SPACE>(line, &pos);
+ }
+
+
+ if (parser.check <SPACE>(line, &pos, true))
+ {
+ for (capability* cap = NULL ;
+ (cap = parser.get <capability>(line, &pos)) != NULL ; )
+ {
+ m_capabilities.push_back(cap);
+
+ parser.check <SPACE>(line, &pos);
+ }
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("date_time");
+
+ string::size_type pos = *currentPos;
+
+ // <"> date_day_fixed "-" date_month "-" date_year
+ parser.check <one_char <'"'> >(line, &pos);
+ parser.check <SPACE>(line, &pos, true);
+
+ utility::auto_ptr <number> nd(parser.get <number>(line, &pos));
+
+ parser.check <one_char <'-'> >(line, &pos);
+
+ utility::auto_ptr <atom> amo(parser.get <atom>(line, &pos));
+
+ parser.check <one_char <'-'> >(line, &pos);
+
+ utility::auto_ptr <number> ny(parser.get <number>(line, &pos));
+
+ parser.check <SPACE>(line, &pos, true);
+
+ // 2digit ":" 2digit ":" 2digit
+ utility::auto_ptr <number> nh(parser.get <number>(line, &pos));
+
+ parser.check <one_char <':'> >(line, &pos);
+
+ utility::auto_ptr <number> nmi(parser.get <number>(line, &pos));
+
+ parser.check <one_char <':'> >(line, &pos);
+
+ utility::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);
+
+ utility::auto_ptr <number> nz(parser.get <number>(line, &pos));
+
+ parser.check <one_char <'"'> >(line, &pos);
+
+
+ m_datetime.setHour(std::min(std::max(nh->value(), 0u), 23u));
+ m_datetime.setMinute(std::min(std::max(nmi->value(), 0u), 59u));
+ m_datetime.setSecond(std::min(std::max(ns->value(), 0u), 59u));
+
+ 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(std::min(std::max(nd->value(), 1u), 31u));
+ m_datetime.setYear(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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("header_list");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ string::size_type 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, 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("section_text");
+
+ string::size_type 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:
+
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("section");
+
+ string::size_type 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(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(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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("address");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("address_list");
+
+ string::size_type 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
+ //
+
+ typedef address_list env_bcc;
+
+
+ //
+ // env_cc ::= "(" 1*address ")" / nil
+ //
+
+ typedef address_list env_cc;
+
+
+ //
+ // env_date ::= nstring
+ //
+
+ typedef nstring env_date;
+
+
+ //
+ // env_from ::= "(" 1*address ")" / nil
+ //
+
+ typedef address_list env_from;
+
+
+ //
+ // env_in_reply_to ::= nstring
+ //
+
+ typedef nstring env_in_reply_to;
+
+
+ //
+ // env_message_id ::= nstring
+ //
+
+ typedef nstring env_message_id;
+
+
+ //
+ // env_reply_to ::= "(" 1*address ")" / nil
+ //
+
+ typedef address_list env_reply_to;
+
+
+ //
+ // env_sender ::= "(" 1*address ")" / nil
+ //
+
+ typedef address_list env_sender;
+
+
+ //
+ // env_subject ::= nstring
+ //
+
+ typedef nstring env_subject;
+
+
+ //
+ // env_to ::= "(" 1*address ")" / nil
+ //
+
+ typedef 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("envelope");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_param_item");
+
+ string::size_type pos = *currentPos;
+
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_param");
+
+ string::size_type pos = *currentPos;
+
+ if (!parser.check <NIL>(line, &pos, true))
+ {
+ parser.check <one_char <'('> >(line, &pos);
+
+ 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));
+ }
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_dsp");
+
+ string::size_type pos = *currentPos;
+
+ if (!parser.check <NIL>(line, &pos, true))
+ {
+ parser.check <one_char <'('> >(line, &pos);
+ 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);
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fld_lang");
+
+ string::size_type 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))
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_fields");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_text");
+
+ string::size_type 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:
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_message");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("media_basic");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_ext_1part");
+
+ string::size_type 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));
+
+ body_extension* ext = NULL;
+
+ while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL)
+ m_body_extensions.push_back(ext);
+ }
+ }
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_ext_mpart");
+
+ string::size_type 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));
+
+ body_extension* ext = NULL;
+
+ while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL)
+ m_body_extensions.push_back(ext);
+ }
+ }
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_basic");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_msg");
+
+ string::size_type 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);
+ m_envelope = parser.get <IMAPParser::envelope>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_body = parser.get <IMAPParser::xbody>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_text");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_1part");
+
+ string::size_type 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);
+
+ *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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body_type_mpart");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("body");
+
+ string::size_type pos = *currentPos;
+
+ parser.check <one_char <'('> >(line, &pos);
+
+ if (!(m_body_type_1part = parser.get <IMAPParser::body_type_1part>(line, &pos, true)))
+ m_body_type_mpart = parser.get <IMAPParser::body_type_mpart>(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
+ //
+
+ 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)
+ {
+ }
+
+ ~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);
+ }
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("msg_att_item");
+
+ string::size_type 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);
+ }
+ }
+ // "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
+ };
+
+ 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;
+
+ public:
+
+ const 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); }
+ };
+
+
+ //
+ // 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("msg_att");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("message_data");
+
+ string::size_type pos = *currentPos;
+
+ nz_number* num = parser.get <nz_number>(line, &pos);
+ m_number = 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:
+
+ const Type type() const { return (m_type); }
+ const unsigned int number() const { return (m_number); }
+ const IMAPParser::msg_att* msg_att() const { return (m_msg_att); }
+ };
+
+
+ //
+ // 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_state");
+
+ string::size_type 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); }
+ const 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_bye");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("resp_cond_auth");
+
+ string::size_type 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:
+
+ const Condition condition() const { return (m_cond); }
+ const IMAPParser::resp_text* resp_text() const { return (m_resp_text); }
+ };
+
+
+ //
+ // status_info ::= status_att SPACE number
+ //
+
+ class status_info : public component
+ {
+ public:
+
+ status_info()
+ : m_status_att(NULL), m_number(NULL)
+ {
+ }
+
+ ~status_info()
+ {
+ delete (m_status_att);
+ delete (m_number);
+ }
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("status_info");
+
+ string::size_type pos = *currentPos;
+
+ m_status_att = parser.get <IMAPParser::status_att>(line, &pos);
+ parser.check <SPACE>(line, &pos);
+ m_number = parser.get <IMAPParser::number>(line, &pos);
+
+ *currentPos = pos;
+ }
+
+ private:
+
+ IMAPParser::status_att* m_status_att;
+ IMAPParser::number* m_number;
+
+ public:
+
+ const IMAPParser::status_att* status_att() const { return (m_status_att); }
+ const IMAPParser::number* number() const { return (m_number); }
+ };
+
+
+ //
+ // 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 number ")" /
+ // 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)
+ {
+ }
+
+ ~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);
+ }
+ }
+
+ void go(IMAPParser& parser, string& line, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("mailbox_data");
+
+ string::size_type 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 number)] ")"
+ //
+ // "(" [status_att SPACE number *(SPACE status_att SPACE number)] ")"
+ 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_info_list.push_back(parser.get <status_info>(line, &pos));
+
+ while (!parser.check <one_char <')'> >(line, &pos, true))
+ {
+ parser.check <SPACE>(line, &pos);
+ m_status_info_list.push_back(parser.get <status_info>(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;
+ std::vector <status_info*> m_status_info_list;
+
+ public:
+
+ const 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 std::vector <status_info*>& status_info_list() const { return (m_status_info_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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_data");
+
+ string::size_type 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);
+
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("continue_req_or_response_data");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_fatal");
+
+ string::size_type 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);
+
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_tagged");
+
+ string::size_type 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);
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response_done");
+
+ string::size_type 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("response");
+
+ string::size_type 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;
+ }
+
+
+ const 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);
+ }
+
+ private:
+
+ std::vector <IMAPParser::continue_req_or_response_data*> m_continue_req_or_response_data;
+ IMAPParser::response_done* m_response_done;
+
+ 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, string::size_type* currentPos)
+ {
+ DEBUG_ENTER_COMPONENT("greeting");
+
+ string::size_type 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;
+ }
+
+ private:
+
+ IMAPParser::resp_cond_auth* m_resp_cond_auth;
+ IMAPParser::resp_cond_bye* m_resp_cond_bye;
+
+ 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)
+ {
+ string::size_type pos = 0;
+ string line = readLine();
+
+ m_literalHandler = lh;
+ response* resp = get <response>(line, &pos);
+ m_literalHandler = NULL;
+
+ return (resp);
+ }
+
+
+ greeting* readGreeting()
+ {
+ string::size_type pos = 0;
+ string line = readLine();
+
+ return get <greeting>(line, &pos);
+ }
+
+
+ //
+ // Get a token and advance
+ //
+
+ template <class TYPE>
+ TYPE* get(string& line, string::size_type* 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, string::size_type* 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, string::size_type* currentPos,
+ const bool noThrow = false)
+ {
+#if DEBUG_RESPONSE
+ DEBUG_RESPONSE_level += " ";
+#endif
+
+ try
+ {
+ resp->go(*this, line, currentPos);
+
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+
+ DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1);
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+ }
+ catch (...)
+ {
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+
+ DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1);
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+
+ delete (resp);
+ if (!noThrow) throw;
+ return (NULL);
+ }
+
+ return static_cast <TYPE*>(resp);
+ }
+
+
+public:
+
+ //
+ // Check a token and advance
+ //
+
+ template <class TYPE>
+ const bool check(string& line, string::size_type* currentPos,
+ const bool noThrow = false)
+ {
+ try
+ {
+ TYPE term;
+ term.go(*this, line, currentPos);
+
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+ }
+ catch (...)
+ {
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+
+ if (!noThrow) throw;
+ return false;
+ }
+
+ return true;
+ }
+
+ template <class TYPE, class ARG_TYPE>
+ const bool checkWithArg(string& line, string::size_type* currentPos,
+ const ARG_TYPE arg, const bool noThrow = false)
+ {
+ try
+ {
+ TYPE term(arg);
+ term.go(*this, line, currentPos);
+
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+ }
+ catch (...)
+ {
+#if DEBUG_RESPONSE
+ std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl;
+ DEBUG_RESPONSE_components.pop_back();
+#endif
+
+ if (!noThrow) throw;
+ return false;
+ }
+
+ return true;
+ }
+
+
+private:
+
+ weak_ref <IMAPTag> m_tag;
+ weak_ref <socket> m_socket;
+
+ utility::progressionListener* m_progress;
+
+ literalHandler* m_literalHandler;
+
+ weak_ref <timeoutHandler> m_timeoutHandler;
+
+
+ string m_buffer;
+ int m_pos;
+
+ string m_lastLine;
+
+public:
+
+ //
+ // Read one line
+ //
+
+ const string readLine()
+ {
+ string::size_type 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;
+
+ while (receiveBuffer.empty())
+ {
+ // Check whether the time-out delay is elapsed
+ if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+ }
+
+ // We have received data: reset the time-out counter
+ m_socket->receive(receiveBuffer);
+
+ if (receiveBuffer.empty()) // buffer is empty
+ {
+ platformDependant::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data ...
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ m_buffer += receiveBuffer;
+ }
+
+
+ void readLiteral(literalHandler::target& buffer, string::size_type count)
+ {
+ string::size_type len = 0;
+ string receiveBuffer;
+
+ if (m_progress)
+ m_progress->start(count);
+
+ if (m_timeoutHandler)
+ m_timeoutHandler->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 (m_timeoutHandler && m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+ }
+
+ // Receive data from the socket
+ m_socket->receive(receiveBuffer);
+
+ if (receiveBuffer.empty()) // buffer is empty
+ {
+ platformDependant::getHandler()->wait();
+ continue;
+ }
+
+ // We have received data: reset the time-out counter
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ if (len + receiveBuffer.length() > count)
+ {
+ const string::size_type 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 progression
+ if (m_progress)
+ m_progress->progress(len, count);
+ }
+
+ if (m_progress)
+ m_progress->stop(count);
+ }
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPPARSER_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPStore.hpp b/vmime/net/imap/IMAPStore.hpp
new file mode 100644
index 00000000..887894f4
--- /dev/null
+++ b/vmime/net/imap/IMAPStore.hpp
@@ -0,0 +1,137 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/store.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/folder.hpp"
+
+#include <ostream>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class IMAPParser;
+class IMAPTag;
+class IMAPConnection;
+class IMAPFolder;
+
+
+/** IMAP store service.
+ */
+
+class IMAPStore : public store
+{
+ friend class IMAPFolder;
+ friend class IMAPMessage;
+ friend class IMAPConnection;
+
+public:
+
+ IMAPStore(ref <session> sess, ref <authenticator> auth);
+ ~IMAPStore();
+
+ const string getProtocolName() const;
+
+ ref <folder> getDefaultFolder();
+ ref <folder> getRootFolder();
+ ref <folder> getFolder(const folder::path& path);
+
+ const bool isValidFolderName(const folder::path::component& name) const;
+
+ static const serviceInfos& getInfosInstance();
+ const serviceInfos& getInfos() const;
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+ void noop();
+
+ const int getCapabilities() const;
+
+private:
+
+ // Connection
+ ref <IMAPConnection> m_connection;
+
+ // Used to request the authentication informations only the
+ // first time, and reuse these informations the next time.
+ ref <class authenticator> m_oneTimeAuth;
+
+
+
+ ref <class authenticator> oneTimeAuthenticator();
+
+
+ ref <IMAPConnection> connection();
+
+
+ void registerFolder(IMAPFolder* folder);
+ void unregisterFolder(IMAPFolder* folder);
+
+ std::list <IMAPFolder*> m_folders;
+
+
+
+ // Service infos
+ class _infos : public serviceInfos
+ {
+ public:
+
+ struct props
+ {
+ // IMAP-specific options
+ // (none)
+
+ // Common properties
+ serviceInfos::property PROPERTY_AUTH_USERNAME;
+ serviceInfos::property PROPERTY_AUTH_PASSWORD;
+
+ serviceInfos::property PROPERTY_SERVER_ADDRESS;
+ serviceInfos::property PROPERTY_SERVER_PORT;
+ serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY;
+
+ serviceInfos::property PROPERTY_TIMEOUT_FACTORY;
+ };
+
+ const props& getProperties() const;
+
+ const string getPropertyPrefix() const;
+ const std::vector <serviceInfos::property> getAvailableProperties() const;
+ };
+
+ static _infos sm_infos;
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPSTORE_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPTag.hpp b/vmime/net/imap/IMAPTag.hpp
new file mode 100644
index 00000000..6b20db75
--- /dev/null
+++ b/vmime/net/imap/IMAPTag.hpp
@@ -0,0 +1,66 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPTAG_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPTAG_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class IMAPTag : public object
+{
+private:
+
+ IMAPTag(const int number);
+ IMAPTag(const IMAPTag& tag);
+
+public:
+
+ IMAPTag();
+
+ IMAPTag& operator++(); // ++IMAPTag
+ const IMAPTag operator++(int); // IMAPTag++
+
+ 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_NET_IMAP_IMAPTAG_HPP_INCLUDED
diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp
new file mode 100644
index 00000000..1fb99e74
--- /dev/null
+++ b/vmime/net/imap/IMAPUtils.hpp
@@ -0,0 +1,68 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED
+#define VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+#include "vmime/dateTime.hpp"
+
+#include "vmime/net/folder.hpp"
+#include "vmime/net/imap/IMAPParser.hpp"
+
+#include <vector>
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+class 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);
+
+ static const string quoteString(const string& text);
+
+ static const int folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list);
+ static const int folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list);
+
+ static const int messageFlagsFromFlags(const IMAPParser::flag_list* list);
+
+ static const string messageFlagList(const int flags);
+
+ static const string listToSet(const std::vector <int>& list, const int max = -1, const bool alreadySorted = false);
+
+ static const string dateTime(const vmime::datetime& date);
+};
+
+
+} // imap
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_IMAP_IMAPUTILS_HPP_INCLUDED
diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp
new file mode 100644
index 00000000..2ff4fca4
--- /dev/null
+++ b/vmime/net/maildir/maildirFolder.hpp
@@ -0,0 +1,178 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED
+#define VMIME_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED
+
+
+#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 maildirFolder : public folder
+{
+private:
+
+ friend class maildirStore;
+ friend class maildirMessage;
+ friend class vmime::creator; // vmime::create <maildirFolder>
+
+
+ maildirFolder(const folder::path& path, weak_ref <maildirStore> store);
+ maildirFolder(const maildirFolder&) : folder() { }
+
+ ~maildirFolder();
+
+public:
+
+ const int getMode() const;
+
+ const int getType();
+
+ const 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);
+
+ const bool exists();
+
+ const bool isOpen() const;
+
+ ref <message> getMessage(const int num);
+ std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
+ std::vector <ref <message> > getMessages(const std::vector <int>& nums);
+ const int getMessageCount();
+
+ ref <folder> getFolder(const folder::path::component& name);
+ std::vector <ref <folder> > getFolders(const bool recursive = false);
+
+ void rename(const folder::path& newPath);
+
+ void deleteMessage(const int num);
+ void deleteMessages(const int from = 1, const int to = -1);
+ void deleteMessages(const std::vector <int>& nums);
+
+ void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+
+ void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+
+ void copyMessage(const folder::path& dest, const int num);
+ void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
+ void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+
+ void status(int& count, int& unseen);
+
+ void expunge();
+
+ ref <folder> getParent();
+
+ weak_ref <const store> getStore() const;
+ weak_ref <store> getStore();
+
+
+ void fetchMessages(std::vector <ref <message> >& msg, const int options, utility::progressionListener* progress = NULL);
+ void fetchMessage(ref <message> msg, const int options);
+
+ const int getFetchCapabilities() const;
+
+private:
+
+ void scanFolder();
+
+ void listFolders(std::vector <ref <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 utility::stream::size_type size, utility::progressionListener* progress);
+
+ void notifyMessagesCopied(const folder::path& dest);
+
+
+ weak_ref <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_NET_MAILDIR_MAILDIRFOLDER_HPP_INCLUDED
diff --git a/vmime/net/maildir/maildirMessage.hpp b/vmime/net/maildir/maildirMessage.hpp
new file mode 100644
index 00000000..82aa9976
--- /dev/null
+++ b/vmime/net/maildir/maildirMessage.hpp
@@ -0,0 +1,103 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED
+#define VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED
+
+
+#include "vmime/net/message.hpp"
+#include "vmime/net/folder.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+class maildirFolder;
+
+
+/** maildir message implementation.
+ */
+
+class maildirMessage : public message
+{
+ friend class maildirFolder;
+ friend class vmime::creator; // vmime::create <maildirMessage>
+
+private:
+
+ maildirMessage(weak_ref <maildirFolder> folder, const int num);
+ maildirMessage(const maildirMessage&) : message() { }
+
+ ~maildirMessage();
+
+public:
+
+ const int getNumber() const;
+
+ const uid getUniqueId() const;
+
+ const int getSize() const;
+
+ const bool isExpunged() const;
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ ref <const header> getHeader() const;
+
+ const int getFlags() const;
+ void setFlags(const int flags, const int mode = FLAG_MODE_SET);
+
+ void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+ void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+
+ void fetchPartHeader(part& p);
+
+private:
+
+ void fetch(weak_ref <maildirFolder> folder, const int options);
+
+ void onFolderClosed();
+
+ ref <header> getOrCreateHeader();
+
+ void extractImpl(utility::outputStream& os, utility::progressionListener* progress, const int start, const int length, const int partialStart, const int partialLength, const bool peek) const;
+
+
+ weak_ref <maildirFolder> m_folder;
+
+ int m_num;
+ int m_size;
+ int m_flags;
+ bool m_expunged;
+ uid m_uid;
+
+ ref <header> m_header;
+ ref <structure> m_structure;
+};
+
+
+} // maildir
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MAILDIR_MAILDIRMESSAGE_HPP_INCLUDED
diff --git a/vmime/net/maildir/maildirStore.hpp b/vmime/net/maildir/maildirStore.hpp
new file mode 100644
index 00000000..32451808
--- /dev/null
+++ b/vmime/net/maildir/maildirStore.hpp
@@ -0,0 +1,114 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED
+#define VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/store.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/folder.hpp"
+
+#include "vmime/utility/file.hpp"
+
+#include <ostream>
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+class maildirFolder;
+
+
+/** maildir store service.
+ */
+
+class maildirStore : public store
+{
+ friend class maildirFolder;
+
+public:
+
+ maildirStore(ref <session> sess, ref <authenticator> auth);
+ ~maildirStore();
+
+ const string getProtocolName() const;
+
+ ref <folder> getDefaultFolder();
+ ref <folder> getRootFolder();
+ ref <folder> getFolder(const folder::path& path);
+
+ const bool isValidFolderName(const folder::path::component& name) const;
+
+ static const serviceInfos& getInfosInstance();
+ const serviceInfos& getInfos() const;
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+ void noop();
+
+ const utility::path& getFileSystemPath() const;
+
+ const int getCapabilities() const;
+
+private:
+
+ void registerFolder(maildirFolder* folder);
+ void unregisterFolder(maildirFolder* folder);
+
+
+ std::list <maildirFolder*> m_folders;
+
+ bool m_connected;
+
+ utility::path m_fsPath;
+
+
+ // Service infos
+ class _infos : public serviceInfos
+ {
+ public:
+
+ struct props
+ {
+ serviceInfos::property PROPERTY_SERVER_ROOTPATH;
+ };
+
+ const props& getProperties() const;
+
+ const string getPropertyPrefix() const;
+ const std::vector <serviceInfos::property> getAvailableProperties() const;
+ };
+
+ static _infos sm_infos;
+};
+
+
+} // maildir
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MAILDIR_MAILDIRSTORE_HPP_INCLUDED
diff --git a/vmime/net/maildir/maildirUtils.hpp b/vmime/net/maildir/maildirUtils.hpp
new file mode 100644
index 00000000..06ecf69b
--- /dev/null
+++ b/vmime/net/maildir/maildirUtils.hpp
@@ -0,0 +1,160 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED
+#define VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED
+
+
+#include "vmime/utility/file.hpp"
+#include "vmime/utility/path.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+class maildirStore;
+
+
+/** Miscellaneous helpers functions for maildir messaging system.
+ */
+
+class 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);
+
+ const bool operator()(const utility::file::path::component& other) const;
+
+ private:
+
+ const utility::file::path::component m_comp;
+ };
+
+ /** Mode for return value of getFolderFSPath(). */
+ enum FolderFSPathMode
+ {
+ FOLDER_PATH_ROOT, /**< Root folder. Eg: ~/Mail/MyFolder */
+ FOLDER_PATH_NEW, /**< Folder containing unread messages. Eg: ~/Mail/MyFolder/new */
+ FOLDER_PATH_CUR, /**< Folder containing messages that have been seen. Eg: ~/Mail/MyFolder/cur */
+ FOLDER_PATH_TMP, /**< Temporary folder used for reliable delivery. Eg: ~/Mail/MyFolder/tmp */
+ FOLDER_PATH_CONTAINER /**< Container for sub-folders. Eg: ~/Mail/.MyFolder.directory */
+ };
+
+ /** Return the path on the filesystem for the folder in specified store.
+ *
+ * @param store parent store
+ * @param folderPath path of the folder
+ * @param mode type of path to return (see FolderFSPathMode)
+ * @return filesystem path for the specified folder
+ */
+ static const utility::file::path getFolderFSPath(weak_ref <maildirStore> store,
+ const utility::path& folderPath, const FolderFSPathMode mode);
+
+ /** Test whether the specified file-system directory corresponds to
+ * a maildir sub-folder. The name of the directory should not start
+ * with '.' to be listed as a sub-folder.
+ *
+ * @param file reference to a file-system directory
+ * @return true if the specified directory is a maildir sub-folder,
+ * false otherwise
+ */
+ static const bool isSubfolderDirectory(const utility::file& file);
+
+ /** 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 const 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 const 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();
+
+private:
+
+ static const vmime::word TMP_DIR;
+ static const vmime::word CUR_DIR;
+ static const vmime::word NEW_DIR;
+};
+
+
+} // maildir
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MAILDIR_MAILDIRUTILS_HPP_INCLUDED
diff --git a/vmime/net/message.hpp b/vmime/net/message.hpp
new file mode 100644
index 00000000..640f945c
--- /dev/null
+++ b/vmime/net/message.hpp
@@ -0,0 +1,292 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_MESSAGE_HPP_INCLUDED
+#define VMIME_NET_MESSAGE_HPP_INCLUDED
+
+
+#include "vmime/header.hpp"
+
+#include "vmime/utility/progressionListener.hpp"
+#include "vmime/utility/stream.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class structure;
+
+
+/** A MIME part in a message.
+ */
+
+class part : public object
+{
+protected:
+
+ part() { }
+ part(const part&) : object() { }
+
+ virtual ~part() { }
+
+public:
+
+ /** Return the structure of this part.
+ *
+ * @return structure of the part
+ */
+ virtual const structure& getStructure() const = 0;
+
+ /** Return the structure of this part.
+ *
+ * @return structure of the part
+ */
+ virtual structure& getStructure() = 0;
+
+ /** Return the header section for this part (you must fetch header
+ * before using this function: see message::fetchPartHeader).
+ *
+ * @return header section
+ */
+ virtual 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 const int getSize() const = 0;
+
+ /** Return the part sequence number (index)
+ *
+ * @return part number
+ */
+ virtual const int getNumber() const = 0; // begin at 1
+
+ /** Return the sub-part at the specified position.
+ * This provide easy access to parts:
+ * Eg: "message->extractPart(message->getStructure()[3][1][2])".
+ *
+ * @param x index of the sub-part
+ * @return sub-part at position 'x'
+ */
+ const part& operator[](const int x) const;
+
+ /** Return the sub-part at the specified position.
+ * This provide easy access to parts:
+ * Eg: "message->extractPart(message->getStructure()[3][1][2])".
+ *
+ * @param x index of the sub-part
+ * @return sub-part at position 'x'
+ */
+ part& operator[](const int x);
+
+ /** Return the number of sub-parts in this part.
+ *
+ * @return number of sub-parts
+ */
+ const int getCount() const;
+};
+
+
+/** Structure of a MIME part/message.
+ */
+
+class structure : public object
+{
+protected:
+
+ structure() { }
+ structure(const structure&) : object() { }
+
+public:
+
+ virtual ~structure() { }
+
+ /** Return the part at the specified position (first
+ * part is at position 1).
+ *
+ * @param x position
+ * @return part at position 'x'
+ */
+ virtual const part& operator[](const int x) const = 0;
+
+ /** Return the part at the specified position (first
+ * part is at position 1).
+ *
+ * @param x position
+ * @return part at position 'x'
+ */
+ virtual part& operator[](const int x) = 0;
+
+ /** Return the number of parts in this part.
+ *
+ * @return number of parts
+ */
+ virtual const int getCount() const = 0;
+};
+
+
+/** Abstract representation of a message in a store/transport service.
+ */
+
+class message : public object
+{
+protected:
+
+ message() { }
+ message(const message&) : object() { }
+
+public:
+
+ virtual ~message() { }
+
+ /** The type for an unique message identifier.
+ */
+ typedef string uid;
+
+ /** Return the MIME structure of the message (must fetch before).
+ *
+ * @return MIME structure of the message
+ */
+ virtual const structure& getStructure() const = 0;
+
+ /** Return the MIME structure of the message (must fetch before).
+ *
+ * @return MIME structure of the message
+ */
+ virtual structure& getStructure() = 0;
+
+ /** Return a reference to the header fields of the message (must fetch before).
+ *
+ * @return header section of the message
+ */
+ virtual ref <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 const int getNumber() const = 0;
+
+ /** Return the unique identified of this message (must fetch before).
+ *
+ * @return UID of the message
+ */
+ virtual const uid getUniqueId() const = 0;
+
+ /** Return the size of the message (must fetch before).
+ *
+ * @return size of the message (in bytes)
+ */
+ virtual const int getSize() const = 0;
+
+ /** Check whether this message has been expunged
+ * (ie: definitively deleted).
+ *
+ * @return true if the message is expunged, false otherwise
+ */
+ virtual const 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_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 const 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 progression 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::progressionListener* progress = NULL, const int start = 0, const int 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 progression listener, or NULL if not used
+ * @param start index of the first byte to retrieve (used for partial fetch)
+ * @param length number of bytes to retrieve (used for partial fetch)
+ * @param peek if true, try not to mark the message as read. This may not
+ * be supported by the protocol (IMAP supports this), but it will NOT throw
+ * an exception if not supported.
+ */
+ virtual void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int 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(part& p) = 0;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_MESSAGE_HPP_INCLUDED
diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp
new file mode 100644
index 00000000..71f4d79f
--- /dev/null
+++ b/vmime/net/pop3/POP3Folder.hpp
@@ -0,0 +1,148 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_POP3_POP3FOLDER_HPP_INCLUDED
+#define VMIME_NET_POP3_POP3FOLDER_HPP_INCLUDED
+
+
+#include <vector>
+#include <map>
+
+#include "vmime/config.hpp"
+#include "vmime/types.hpp"
+
+#include "vmime/net/folder.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+class POP3Store;
+class POP3Message;
+
+
+/** POP3 folder implementation.
+ */
+
+class POP3Folder : public folder
+{
+private:
+
+ friend class POP3Store;
+ friend class POP3Message;
+ friend class vmime::creator; // vmime::create <POP3Folder>
+
+ POP3Folder(const folder::path& path, POP3Store* store);
+ POP3Folder(const POP3Folder&) : folder() { }
+
+ ~POP3Folder();
+
+public:
+
+ const int getMode() const;
+
+ const int getType();
+
+ const 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);
+
+ const bool exists();
+
+ const bool isOpen() const;
+
+ ref <message> getMessage(const int num);
+ std::vector <ref <message> > getMessages(const int from = 1, const int to = -1);
+ std::vector <ref <message> > getMessages(const std::vector <int>& nums);
+ const int getMessageCount();
+
+ ref <folder> getFolder(const folder::path::component& name);
+ std::vector <ref <folder> > getFolders(const bool recursive = false);
+
+ void rename(const folder::path& newPath);
+
+ void deleteMessage(const int num);
+ void deleteMessages(const int from = 1, const int to = -1);
+ void deleteMessages(const std::vector <int>& nums);
+
+ void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET);
+ void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET);
+
+ void addMessage(ref <vmime::message> msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+ void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL);
+
+ void copyMessage(const folder::path& dest, const int num);
+ void copyMessages(const folder::path& dest, const int from = 1, const int to = -1);
+ void copyMessages(const folder::path& dest, const std::vector <int>& nums);
+
+ void status(int& count, int& unseen);
+
+ void expunge();
+
+ ref <folder> getParent();
+
+ weak_ref <const store> getStore() const;
+ weak_ref <store> getStore();
+
+
+ void fetchMessages(std::vector <ref <message> >& msg, const int options, utility::progressionListener* progress = NULL);
+ void fetchMessage(ref <message> msg, const int options);
+
+ const int getFetchCapabilities() const;
+
+private:
+
+ void registerMessage(POP3Message* msg);
+ void unregisterMessage(POP3Message* msg);
+
+ void onStoreDisconnected();
+
+ void onClose();
+
+ void parseMultiListOrUidlResponse(const string& response, std::map <int, string>& result);
+
+
+ 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_NET_POP3_POP3FOLDER_HPP_INCLUDED
diff --git a/vmime/net/pop3/POP3Message.hpp b/vmime/net/pop3/POP3Message.hpp
new file mode 100644
index 00000000..f4c48bb6
--- /dev/null
+++ b/vmime/net/pop3/POP3Message.hpp
@@ -0,0 +1,98 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED
+#define VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/message.hpp"
+#include "vmime/net/folder.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+class POP3Folder;
+
+
+/** POP3 message implementation.
+ */
+
+class POP3Message : public message
+{
+private:
+
+ friend class POP3Folder;
+ friend class vmime::creator; // vmime::create <POP3Message>
+
+ POP3Message(POP3Folder* folder, const int num);
+ POP3Message(const POP3Message&) : message() { }
+
+ ~POP3Message();
+
+public:
+
+ const int getNumber() const;
+
+ const uid getUniqueId() const;
+
+ const int getSize() const;
+
+ const bool isExpunged() const;
+
+ const structure& getStructure() const;
+ structure& getStructure();
+
+ ref <const header> getHeader() const;
+
+ const int getFlags() const;
+ void setFlags(const int flags, const int mode = FLAG_MODE_SET);
+
+ void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+ void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1, const bool peek = false) const;
+
+ void fetchPartHeader(part& p);
+
+private:
+
+ void fetch(POP3Folder* folder, const int options);
+
+ void onFolderClosed();
+
+ POP3Folder* m_folder;
+ int m_num;
+ uid m_uid;
+ int m_size;
+
+ bool m_deleted;
+
+ ref <header> m_header;
+};
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_POP3_POP3MESSAGE_HPP_INCLUDED
diff --git a/vmime/net/pop3/POP3Store.hpp b/vmime/net/pop3/POP3Store.hpp
new file mode 100644
index 00000000..e50bbeef
--- /dev/null
+++ b/vmime/net/pop3/POP3Store.hpp
@@ -0,0 +1,138 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_POP3_POP3STORE_HPP_INCLUDED
+#define VMIME_NET_POP3_POP3STORE_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/store.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+#include "vmime/utility/stream.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+class POP3Folder;
+
+
+/** POP3 store service.
+ */
+
+class POP3Store : public store
+{
+ friend class POP3Folder;
+ friend class POP3Message;
+
+public:
+
+ POP3Store(ref <session> sess, ref <authenticator> auth);
+ ~POP3Store();
+
+ const string getProtocolName() const;
+
+ ref <folder> getDefaultFolder();
+ ref <folder> getRootFolder();
+ ref <folder> getFolder(const folder::path& path);
+
+ const bool isValidFolderName(const folder::path::component& name) const;
+
+ static const serviceInfos& getInfosInstance();
+ const serviceInfos& getInfos() const;
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+ void noop();
+
+ const int getCapabilities() const;
+
+private:
+
+ static const bool isSuccessResponse(const string& buffer);
+ static const bool stripFirstLine(const string& buffer, string& result, string* firstLine = NULL);
+ static void stripResponseCode(const string& buffer, string& result);
+
+ void sendRequest(const string& buffer, const bool end = true);
+ void readResponse(string& buffer, const bool multiLine, utility::progressionListener* progress = NULL);
+ void readResponse(utility::outputStream& os, utility::progressionListener* progress = NULL, const int predictedSize = 0);
+
+ static const bool checkTerminator(string& buffer, const bool multiLine);
+ static const bool checkOneTerminator(string& buffer, const string& term);
+
+ void internalDisconnect();
+
+
+ void registerFolder(POP3Folder* folder);
+ void unregisterFolder(POP3Folder* folder);
+
+ std::list <POP3Folder*> m_folders;
+
+
+ ref <socket> m_socket;
+ bool m_authentified;
+
+ ref <timeoutHandler> m_timeoutHandler;
+
+
+ // Service infos
+ class _infos : public serviceInfos
+ {
+ public:
+
+ struct props
+ {
+ // POP3-specific options
+ serviceInfos::property PROPERTY_OPTIONS_APOP;
+ serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK;
+
+ // Common properties
+ serviceInfos::property PROPERTY_AUTH_USERNAME;
+ serviceInfos::property PROPERTY_AUTH_PASSWORD;
+
+ serviceInfos::property PROPERTY_SERVER_ADDRESS;
+ serviceInfos::property PROPERTY_SERVER_PORT;
+ serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY;
+
+ serviceInfos::property PROPERTY_TIMEOUT_FACTORY;
+ };
+
+ const props& getProperties() const;
+
+ const string getPropertyPrefix() const;
+ const std::vector <serviceInfos::property> getAvailableProperties() const;
+ };
+
+ static _infos sm_infos;
+};
+
+
+} // pop3
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_POP3_POP3STORE_HPP_INCLUDED
diff --git a/vmime/net/sendmail/sendmailTransport.hpp b/vmime/net/sendmail/sendmailTransport.hpp
new file mode 100644
index 00000000..292f5ebe
--- /dev/null
+++ b/vmime/net/sendmail/sendmailTransport.hpp
@@ -0,0 +1,103 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED
+#define VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/transport.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+
+#if VMIME_BUILTIN_PLATFORM_POSIX
+
+
+namespace vmime {
+namespace net {
+namespace sendmail {
+
+
+/** Sendmail local transport service.
+ */
+
+class sendmailTransport : public transport
+{
+public:
+
+ sendmailTransport(ref <session> sess, ref <authenticator> auth);
+ ~sendmailTransport();
+
+ const string getProtocolName() const;
+
+ static const serviceInfos& getInfosInstance();
+ const serviceInfos& getInfos() const;
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+ void noop();
+
+ void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL);
+
+private:
+
+ void internalDisconnect();
+
+ void internalSend(const std::vector <string> args, utility::inputStream& is,
+ const utility::stream::size_type size, utility::progressionListener* progress);
+
+
+ string m_sendmailPath;
+
+ bool m_connected;
+
+
+ // Service infos
+ class _infos : public serviceInfos
+ {
+ public:
+
+ struct props
+ {
+ serviceInfos::property PROPERTY_BINPATH;
+ };
+
+ const props& getProperties() const;
+
+ const string getPropertyPrefix() const;
+ const std::vector <serviceInfos::property> getAvailableProperties() const;
+ };
+
+ static _infos sm_infos;
+};
+
+
+} // sendmail
+} // net
+} // vmime
+
+
+#endif // VMIME_BUILTIN_PLATFORM_POSIX
+
+
+#endif // VMIME_NET_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED
diff --git a/vmime/net/service.hpp b/vmime/net/service.hpp
new file mode 100644
index 00000000..cfb5c736
--- /dev/null
+++ b/vmime/net/service.hpp
@@ -0,0 +1,161 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SERVICE_HPP_INCLUDED
+#define VMIME_NET_SERVICE_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+#include "vmime/net/session.hpp"
+#include "vmime/net/authenticator.hpp"
+
+#include "vmime/net/serviceFactory.hpp"
+#include "vmime/net/serviceInfos.hpp"
+
+#include "vmime/utility/progressionListener.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** Base class for messaging services.
+ */
+
+class service : public object
+{
+protected:
+
+ service(ref <session> sess, const serviceInfos& infos, ref <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 const 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
+ */
+ ref <const session> getSession() const;
+
+ /** Return the session object associated with this service instance.
+ *
+ * @return session object
+ */
+ ref <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 const 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
+ */
+ ref <const authenticator> getAuthenticator() const;
+
+ /** Return the authenticator object used with this service instance.
+ *
+ * @return authenticator object
+ */
+ ref <authenticator> getAuthenticator();
+
+ /** 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;
+ }
+
+#ifndef VMIME_BUILDING_DOC
+ // Basic service registerer
+ template <class S>
+ class initializer
+ {
+ public:
+
+ initializer(const string& protocol)
+ {
+ serviceFactory::getInstance()->
+ template registerServiceByProtocol <S>(protocol);
+ }
+ };
+#endif // VMIME_BUILDING_DOC
+
+private:
+
+ ref <session> m_session;
+ ref <authenticator> m_auth;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SERVICE_HPP_INCLUDED
diff --git a/vmime/net/serviceFactory.hpp b/vmime/net/serviceFactory.hpp
new file mode 100644
index 00000000..2eb04e0e
--- /dev/null
+++ b/vmime/net/serviceFactory.hpp
@@ -0,0 +1,188 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SERVICEFACTORY_HPP_INCLUDED
+#define VMIME_NET_SERVICEFACTORY_HPP_INCLUDED
+
+
+#include <map>
+
+#include "vmime/types.hpp"
+#include "vmime/base.hpp"
+
+#include "vmime/utility/stringUtils.hpp"
+#include "vmime/utility/url.hpp"
+
+#include "vmime/net/serviceInfos.hpp"
+#include "vmime/net/authenticator.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+#include "vmime/utility/progressionListener.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+class service;
+class session;
+
+
+/** A factory to create 'service' objects for a specified protocol.
+ */
+
+class serviceFactory
+{
+private:
+
+ serviceFactory();
+ ~serviceFactory();
+
+public:
+
+ static serviceFactory* getInstance();
+
+ /** Information about a registered service. */
+ class registeredService : public object
+ {
+ friend class serviceFactory;
+
+ protected:
+
+ virtual ~registeredService() { }
+
+ public:
+
+ virtual ref <service> create(ref <session> sess, ref <authenticator> auth) const = 0;
+
+ virtual const string& getName() const = 0;
+ virtual const serviceInfos& getInfos() const = 0;
+ };
+
+private:
+
+ template <class S>
+ class registeredServiceImpl : public registeredService
+ {
+ friend class serviceFactory;
+ friend class vmime::creator;
+
+ protected:
+
+ registeredServiceImpl(const string& name)
+ : m_name(name), m_servInfos(S::getInfosInstance())
+ {
+ }
+
+ public:
+
+ ref <service> create(ref <session> sess, ref <authenticator> auth) const
+ {
+ return vmime::create <S>(sess, auth);
+ }
+
+ const serviceInfos& getInfos() const
+ {
+ return (m_servInfos);
+ }
+
+ const string& getName() const
+ {
+ return (m_name);
+ }
+
+ private:
+
+ const string m_name;
+ const serviceInfos& m_servInfos;
+ };
+
+ std::vector <ref <registeredService> > m_services;
+
+public:
+
+ /** Register a new service by its protocol name.
+ *
+ * @param protocol protocol name
+ */
+ template <class S>
+ void registerServiceByProtocol(const string& protocol)
+ {
+ const string name = utility::stringUtils::toLower(protocol);
+ m_services.push_back(vmime::create <registeredServiceImpl <S> >(name));
+ }
+
+ /** 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
+ * @throw exceptions::no_service_available if no service is registered
+ * for this protocol
+ */
+ ref <service> create(ref <session> sess, const string& protocol, ref <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
+ * @throw exceptions::no_service_available if no service is registered
+ * for this protocol
+ */
+ ref <service> create(ref <session> sess, const utility::url& u, ref <authenticator> auth = NULL);
+
+ /** Return information about a registered protocol.
+ *
+ * @param protocol protocol name
+ * @return information about this protocol
+ * @throw exceptions::no_service_available if no service is registered
+ * for this protocol
+ */
+ ref <const registeredService> getServiceByProtocol(const string& protocol) const;
+
+ /** Return the number of registered services.
+ *
+ * @return number of registered services
+ */
+ const int 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
+ */
+ ref <const registeredService> getServiceAt(const int pos) const;
+
+ /** Return a list of all registered services.
+ *
+ * @return list of registered services
+ */
+ const std::vector <ref <const registeredService> > getServiceList() const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SERVICEFACTORY_HPP_INCLUDED
diff --git a/vmime/net/serviceInfos.hpp b/vmime/net/serviceInfos.hpp
new file mode 100644
index 00000000..8a343667
--- /dev/null
+++ b/vmime/net/serviceInfos.hpp
@@ -0,0 +1,228 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SERVICEINFOS_HPP_INCLUDED
+#define VMIME_NET_SERVICEINFOS_HPP_INCLUDED
+
+
+#include <vector>
+
+#include "vmime/types.hpp"
+
+#include "vmime/net/session.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** Stores information about a messaging service.
+ */
+
+class 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 'server.socket-factory' used
+ * to indicate which factory to use to instanciate
+ * new socket objects. */
+ static const property SERVER_SOCKETFACTORY;
+
+ /** The common property 'auth.username' which is the
+ * username used to authenticate with the server. */
+ static const property AUTH_USERNAME;
+
+ /** The common property 'auth.password' which is the
+ * password used to authenticate with the server. */
+ static const property AUTH_PASSWORD;
+
+ /** The common property 'timeout.factory' used to
+ * specify which factory to use to instanciate
+ * time-out handler objects. If none is specified,
+ * no time-out handler is used. */
+ static const property TIMEOUT_FACTORY;
+
+
+ /** Value types.
+ */
+ enum Types
+ {
+ TYPE_INTEGER, /*< Integer number. */
+ TYPE_STRING, /*< Character string. */
+ TYPE_BOOL, /*< 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
+ */
+ const Types getType() const;
+
+ /** Return the attributes of the property (see
+ * serviceInfos::property::Types constants).
+ *
+ * @return property attributes
+ */
+ const 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(ref <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
+ */
+ const bool hasProperty(ref <session> s, const property& p) const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SERVICEINFOS_HPP_INCLUDED
diff --git a/vmime/net/session.hpp b/vmime/net/session.hpp
new file mode 100644
index 00000000..c01ae5f2
--- /dev/null
+++ b/vmime/net/session.hpp
@@ -0,0 +1,135 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SESSION_HPP_INCLUDED
+#define VMIME_NET_SESSION_HPP_INCLUDED
+
+
+#include "vmime/net/authenticator.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 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
+ */
+ ref <transport> getTransport(ref <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
+ */
+ ref <transport> getTransport(const string& protocol, ref <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
+ */
+ ref <transport> getTransport(const utility::url& url, ref <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
+ */
+ ref <store> getStore(ref <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
+ */
+ ref <store> getStore(const string& protocol, ref <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
+ */
+ ref <store> getStore(const utility::url& url, ref <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();
+
+private:
+
+ propertySet m_props;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SESSION_HPP_INCLUDED
diff --git a/vmime/net/simpleAuthenticator.hpp b/vmime/net/simpleAuthenticator.hpp
new file mode 100644
index 00000000..e60b5174
--- /dev/null
+++ b/vmime/net/simpleAuthenticator.hpp
@@ -0,0 +1,62 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
+#define VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/net/authenticator.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** Basic implementation for an authenticator.
+ */
+
+class simpleAuthenticator : public authenticator
+{
+public:
+
+ simpleAuthenticator();
+ simpleAuthenticator(const string& username, const string& password);
+
+public:
+
+ const string& getUsername() const;
+ void setUsername(const string& username);
+
+ const string& getPassword() const;
+ void setPassword(const string& password);
+
+private:
+
+ string m_username;
+ string m_password;
+
+ const authenticationInfos getAuthInfos() const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
diff --git a/vmime/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp
new file mode 100644
index 00000000..1743ee7c
--- /dev/null
+++ b/vmime/net/smtp/SMTPTransport.hpp
@@ -0,0 +1,113 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED
+#define VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED
+
+
+#include "vmime/config.hpp"
+
+#include "vmime/net/transport.hpp"
+#include "vmime/net/socket.hpp"
+#include "vmime/net/timeoutHandler.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+/** SMTP transport service.
+ */
+
+class SMTPTransport : public transport
+{
+public:
+
+ SMTPTransport(ref <session> sess, ref <authenticator> auth);
+ ~SMTPTransport();
+
+ const string getProtocolName() const;
+
+ static const serviceInfos& getInfosInstance();
+ const serviceInfos& getInfos() const;
+
+ void connect();
+ const bool isConnected() const;
+ void disconnect();
+
+ void noop();
+
+ void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL);
+
+private:
+
+ static const int responseCode(const string& response);
+ static const string responseText(const string& response);
+
+ void sendRequest(const string& buffer, const bool end = true);
+
+ void readResponse(string& buffer);
+
+ void internalDisconnect();
+
+ ref <socket> m_socket;
+ bool m_authentified;
+ bool m_extendedSMTP;
+
+ ref <timeoutHandler> m_timeoutHandler;
+
+
+ // Service infos
+ class _infos : public serviceInfos
+ {
+ public:
+
+ struct props
+ {
+ // SMTP-specific options
+ serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH;
+
+ // Common properties
+ serviceInfos::property PROPERTY_AUTH_USERNAME;
+ serviceInfos::property PROPERTY_AUTH_PASSWORD;
+
+ serviceInfos::property PROPERTY_SERVER_ADDRESS;
+ serviceInfos::property PROPERTY_SERVER_PORT;
+ serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY;
+
+ serviceInfos::property PROPERTY_TIMEOUT_FACTORY;
+ };
+
+ const props& getProperties() const;
+
+ const string getPropertyPrefix() const;
+ const std::vector <serviceInfos::property> getAvailableProperties() const;
+ };
+
+ static _infos sm_infos;
+};
+
+
+} // smtp
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SMTP_SMTPTRANSPORT_HPP_INCLUDED
diff --git a/vmime/net/socket.hpp b/vmime/net/socket.hpp
new file mode 100644
index 00000000..a79eac1d
--- /dev/null
+++ b/vmime/net/socket.hpp
@@ -0,0 +1,112 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_SOCKET_HPP_INCLUDED
+#define VMIME_NET_SOCKET_HPP_INCLUDED
+
+
+#include "vmime/base.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** Interface for connecting to servers.
+ */
+
+class socket : public object
+{
+public:
+
+ 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 const 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 const int receiveRaw(char* buffer, const int count) = 0;
+
+ /** Send (text) data to the socket.
+ *
+ * @param buffer data to send
+ */
+ virtual void send(const string& buffer) = 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 char* buffer, const int count) = 0;
+
+protected:
+
+ socket() { }
+
+private:
+
+ socket(const socket&) : object() { }
+};
+
+
+/** A class to create 'socket' objects.
+ */
+
+class socketFactory
+{
+public:
+
+ virtual ~socketFactory() { }
+
+ virtual ref <socket> create() = 0;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_SOCKET_HPP_INCLUDED
diff --git a/vmime/net/store.hpp b/vmime/net/store.hpp
new file mode 100644
index 00000000..5855c76d
--- /dev/null
+++ b/vmime/net/store.hpp
@@ -0,0 +1,102 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_STORE_HPP_INCLUDED
+#define VMIME_NET_STORE_HPP_INCLUDED
+
+
+#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 store : public service
+{
+protected:
+
+ store(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+ : service(sess, infos, auth) { }
+
+public:
+
+ /** Return the default folder. This is protocol dependant
+ * and usually is the INBOX folder.
+ *
+ * @return default folder
+ */
+ virtual ref <folder> getDefaultFolder() = 0;
+
+ /** Return the root folder. This is protocol dependant
+ * and usually is the user's mail drop root folder.
+ *
+ * @return root folder
+ */
+ virtual ref <folder> getRootFolder() = 0;
+
+ /** Return the folder specified by the path.
+ *
+ * @param path absolute folder path
+ * @return folder at the specified path
+ */
+ virtual ref <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 const 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 const int getCapabilities() const = 0;
+
+
+ const Type getType() const { return (TYPE_STORE); }
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_STORE_HPP_INCLUDED
diff --git a/vmime/net/timeoutHandler.hpp b/vmime/net/timeoutHandler.hpp
new file mode 100644
index 00000000..5270978c
--- /dev/null
+++ b/vmime/net/timeoutHandler.hpp
@@ -0,0 +1,77 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED
+#define VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+namespace vmime {
+namespace net {
+
+
+/** A class to manage time-out in messaging services.
+ */
+
+class 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 const 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 const bool handleTimeOut() = 0;
+};
+
+
+/** A class to create 'timeoutHandler' objects.
+ */
+
+class timeoutHandlerFactory
+{
+public:
+
+ virtual ~timeoutHandlerFactory() { }
+
+ virtual ref <timeoutHandler> create() = 0;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_TIMEOUTHANDLER_HPP_INCLUDED
diff --git a/vmime/net/transport.hpp b/vmime/net/transport.hpp
new file mode 100644
index 00000000..f4be446d
--- /dev/null
+++ b/vmime/net/transport.hpp
@@ -0,0 +1,75 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef VMIME_NET_TRANSPORT_HPP_INCLUDED
+#define VMIME_NET_TRANSPORT_HPP_INCLUDED
+
+
+#include "vmime/net/service.hpp"
+#include "vmime/utility/stream.hpp"
+
+
+namespace vmime {
+
+class message;
+class mailbox;
+class mailboxList;
+
+namespace net {
+
+
+/** A transport service.
+ * Encapsulate protocols that can send messages.
+ */
+
+class transport : public service
+{
+protected:
+
+ transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth);
+
+public:
+
+ /** Send a message over this transport service.
+ *
+ * @param msg message to send
+ * @param progress progression listener, or NULL if not used
+ */
+ virtual void send(ref <vmime::message> msg, utility::progressionListener* progress = NULL);
+
+ /** Send a message over this transport service.
+ *
+ * @param expeditor expeditor mailbox
+ * @param recipients list of recipient mailboxes
+ * @param is input stream provding message data (header + body)
+ * @param size size of the message data
+ * @param progress progression listener, or NULL if not used
+ */
+ virtual void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL) = 0;
+
+
+ const Type getType() const;
+};
+
+
+} // net
+} // vmime
+
+
+#endif // VMIME_NET_TRANSPORT_HPP_INCLUDED
diff --git a/vmime/platformDependant.hpp b/vmime/platformDependant.hpp
index b6de8e76..0492f8e6 100644
--- a/vmime/platformDependant.hpp
+++ b/vmime/platformDependant.hpp
@@ -27,8 +27,8 @@
#include "vmime/charset.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
- #include "vmime/messaging/socket.hpp"
- #include "vmime/messaging/timeoutHandler.hpp"
+ #include "vmime/net/socket.hpp"
+ #include "vmime/net/timeoutHandler.hpp"
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
@@ -110,7 +110,7 @@ public:
* session object
* @return socket factory
*/
- virtual messaging::socketFactory* getSocketFactory(const string& name = "default") const = 0;
+ virtual net::socketFactory* getSocketFactory(const string& name = "default") const = 0;
/** Return a pointer to a timeout-handler factory for the specified name.
* The returned object will not be deleted by VMime, so it can be a
@@ -124,7 +124,7 @@ public:
* @param name time-out type name
* @return time-out factory
*/
- virtual messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const string& name = "default") const = 0;
+ virtual net::timeoutHandlerFactory* getTimeoutHandlerFactory(const string& name = "default") const = 0;
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
/** Return a pointer to a factory that creates file-system objects.
diff --git a/vmime/platforms/posix/posixHandler.hpp b/vmime/platforms/posix/posixHandler.hpp
index 62500d9a..da8344fb 100644
--- a/vmime/platforms/posix/posixHandler.hpp
+++ b/vmime/platforms/posix/posixHandler.hpp
@@ -57,9 +57,9 @@ public:
const unsigned int getProcessId() const;
#if VMIME_HAVE_MESSAGING_FEATURES
- vmime::messaging::socketFactory* getSocketFactory(const vmime::string& name) const;
+ vmime::net::socketFactory* getSocketFactory(const vmime::string& name) const;
- vmime::messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const vmime::string& name) const;
+ vmime::net::timeoutHandlerFactory* getTimeoutHandlerFactory(const vmime::string& name) const;
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
diff --git a/vmime/platforms/posix/posixSocket.hpp b/vmime/platforms/posix/posixSocket.hpp
index 909ada6a..93315cf9 100644
--- a/vmime/platforms/posix/posixSocket.hpp
+++ b/vmime/platforms/posix/posixSocket.hpp
@@ -21,7 +21,7 @@
#define VMIME_PLATFORMS_POSIX_SOCKET_HPP_INCLUDED
-#include "vmime/messaging/socket.hpp"
+#include "vmime/net/socket.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
@@ -32,7 +32,7 @@ namespace platforms {
namespace posix {
-class posixSocket : public vmime::messaging::socket
+class posixSocket : public vmime::net::socket
{
public:
@@ -57,11 +57,11 @@ private:
-class posixSocketFactory : public vmime::messaging::socketFactory
+class posixSocketFactory : public vmime::net::socketFactory
{
public:
- ref <vmime::messaging::socket> create();
+ ref <vmime::net::socket> create();
};
diff --git a/vmime/platforms/windows/windowsHandler.hpp b/vmime/platforms/windows/windowsHandler.hpp
index 0f2c5b88..62ebe093 100644
--- a/vmime/platforms/windows/windowsHandler.hpp
+++ b/vmime/platforms/windows/windowsHandler.hpp
@@ -56,9 +56,9 @@ public:
const unsigned int getProcessId() const;
#if VMIME_HAVE_MESSAGING_FEATURES
- vmime::messaging::socketFactory* getSocketFactory(const vmime::string& name) const;
+ vmime::net::socketFactory* getSocketFactory(const vmime::string& name) const;
- vmime::messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const vmime::string& name) const;
+ vmime::net::timeoutHandlerFactory* getTimeoutHandlerFactory(const vmime::string& name) const;
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
diff --git a/vmime/platforms/windows/windowsSocket.hpp b/vmime/platforms/windows/windowsSocket.hpp
index bbf76643..e5c3c44a 100644
--- a/vmime/platforms/windows/windowsSocket.hpp
+++ b/vmime/platforms/windows/windowsSocket.hpp
@@ -22,7 +22,7 @@
#include <winsock2.h>
-#include "vmime/messaging/socket.hpp"
+#include "vmime/net/socket.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
@@ -33,7 +33,7 @@ namespace platforms {
namespace windows {
-class windowsSocket : public vmime::messaging::socket
+class windowsSocket : public vmime::net::socket
{
public:
windowsSocket();
@@ -59,11 +59,11 @@ private:
-class windowsSocketFactory : public vmime::messaging::socketFactory
+class windowsSocketFactory : public vmime::net::socketFactory
{
public:
- ref <vmime::messaging::socket> create();
+ ref <vmime::net::socket> create();
};
diff --git a/vmime/types.hpp b/vmime/types.hpp
index 1028fed3..38485f9a 100644
--- a/vmime/types.hpp
+++ b/vmime/types.hpp
@@ -47,6 +47,10 @@ namespace vmime
using vmime::utility::null_ref;
extern const null_ref null;
+
+ // For compatibility with versions <= 0.7.1 (deprecated)
+ namespace net { }
+ namespace messaging = net;
}
diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp
index 5dd9d4dc..4b1cba2d 100644
--- a/vmime/utility/stream.hpp
+++ b/vmime/utility/stream.hpp
@@ -33,9 +33,9 @@
#if VMIME_HAVE_MESSAGING_FEATURES
namespace vmime {
- namespace messaging {
+ namespace net {
class socket; // forward reference
- } // messaging
+ } // net
} // vmime
#endif
@@ -336,7 +336,7 @@ class outputStreamSocketAdapter : public outputStream
{
public:
- outputStreamSocketAdapter(messaging::socket& sok);
+ outputStreamSocketAdapter(net::socket& sok);
void write(const value_type* const data, const size_type count);
@@ -344,7 +344,7 @@ private:
outputStreamSocketAdapter(const outputStreamSocketAdapter&);
- messaging::socket& m_socket;
+ net::socket& m_socket;
};
@@ -355,7 +355,7 @@ class inputStreamSocketAdapter : public inputStream
{
public:
- inputStreamSocketAdapter(messaging::socket& sok);
+ inputStreamSocketAdapter(net::socket& sok);
const bool eof() const;
void reset();
@@ -366,7 +366,7 @@ private:
inputStreamSocketAdapter(const inputStreamSocketAdapter&);
- messaging::socket& m_socket;
+ net::socket& m_socket;
};
diff --git a/vmime/vmime.hpp b/vmime/vmime.hpp
index b5cac466..3a6b6474 100644
--- a/vmime/vmime.hpp
+++ b/vmime/vmime.hpp
@@ -88,19 +88,19 @@
// Messaging features
#if VMIME_HAVE_MESSAGING_FEATURES
- #include "vmime/messaging/socket.hpp"
+ #include "vmime/net/socket.hpp"
- #include "vmime/messaging/service.hpp"
- #include "vmime/messaging/store.hpp"
- #include "vmime/messaging/transport.hpp"
+ #include "vmime/net/service.hpp"
+ #include "vmime/net/store.hpp"
+ #include "vmime/net/transport.hpp"
- #include "vmime/messaging/session.hpp"
- #include "vmime/messaging/authenticator.hpp"
- #include "vmime/messaging/defaultAuthenticator.hpp"
- #include "vmime/messaging/simpleAuthenticator.hpp"
+ #include "vmime/net/session.hpp"
+ #include "vmime/net/authenticator.hpp"
+ #include "vmime/net/defaultAuthenticator.hpp"
+ #include "vmime/net/simpleAuthenticator.hpp"
- #include "vmime/messaging/folder.hpp"
- #include "vmime/messaging/message.hpp"
+ #include "vmime/net/folder.hpp"
+ #include "vmime/net/message.hpp"
#endif // VMIME_HAVE_MESSAGING_FEATURES