aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2005-09-17 09:08:45 +0000
committerVincent Richard <[email protected]>2005-09-17 09:08:45 +0000
commit0edaa87860dbfd4871c597a9f2fbec07fca67ed6 (patch)
tree0babae1fdc77724b92be66d90a03e0891dc8d0b8
parentInput buffer size. (diff)
downloadvmime-0edaa87860dbfd4871c597a9f2fbec07fca67ed6.tar.gz
vmime-0edaa87860dbfd4871c597a9f2fbec07fca67ed6.zip
SASL authentication.
-rw-r--r--ChangeLog5
-rw-r--r--SConstruct136
-rw-r--r--examples/example6.cpp63
-rw-r--r--src/exception.cpp48
-rw-r--r--src/net/authenticationInfos.cpp52
-rw-r--r--src/net/authenticator.cpp33
-rw-r--r--src/net/defaultAuthenticator.cpp43
-rw-r--r--src/net/imap/IMAPConnection.cpp312
-rw-r--r--src/net/imap/IMAPFolder.cpp2
-rw-r--r--src/net/imap/IMAPStore.cpp80
-rw-r--r--src/net/maildir/maildirStore.cpp15
-rw-r--r--src/net/pop3/POP3Store.cpp426
-rw-r--r--src/net/sendmail/sendmailTransport.cpp13
-rw-r--r--src/net/service.cpp29
-rw-r--r--src/net/serviceFactory.cpp6
-rw-r--r--src/net/session.cpp16
-rw-r--r--src/net/simpleAuthenticator.cpp69
-rw-r--r--src/net/smtp/SMTPTransport.cpp406
-rw-r--r--src/net/transport.cpp2
-rw-r--r--src/security/defaultAuthenticator.cpp98
-rw-r--r--src/security/sasl/SASLContext.cpp189
-rw-r--r--src/security/sasl/SASLMechanismFactory.cpp131
-rw-r--r--src/security/sasl/SASLSession.cpp179
-rw-r--r--src/security/sasl/SASLSocket.cpp167
-rw-r--r--src/security/sasl/builtinSASLMechanism.cpp182
-rw-r--r--src/security/sasl/defaultSASLAuthenticator.cpp139
-rw-r--r--src/utility/stream.cpp61
-rw-r--r--vmime/exception.hpp52
-rw-r--r--vmime/net/authenticator.hpp54
-rw-r--r--vmime/net/imap/IMAPConnection.hpp17
-rw-r--r--vmime/net/imap/IMAPParser.hpp71
-rw-r--r--vmime/net/imap/IMAPStore.hpp14
-rw-r--r--vmime/net/maildir/maildirStore.hpp2
-rw-r--r--vmime/net/pop3/POP3Store.hpp21
-rw-r--r--vmime/net/sendmail/sendmailTransport.hpp2
-rw-r--r--vmime/net/service.hpp15
-rw-r--r--vmime/net/serviceFactory.hpp21
-rw-r--r--vmime/net/session.hpp23
-rw-r--r--vmime/net/simpleAuthenticator.hpp62
-rw-r--r--vmime/net/smtp/SMTPTransport.hpp24
-rw-r--r--vmime/net/store.hpp2
-rw-r--r--vmime/net/transport.hpp2
-rw-r--r--vmime/security/authenticator.hpp116
-rw-r--r--vmime/security/defaultAuthenticator.hpp (renamed from vmime/net/defaultAuthenticator.hpp)39
-rw-r--r--vmime/security/sasl/SASLAuthenticator.hpp84
-rw-r--r--vmime/security/sasl/SASLContext.hpp116
-rw-r--r--vmime/security/sasl/SASLMechanism.hpp119
-rw-r--r--vmime/security/sasl/SASLMechanismFactory.hpp131
-rw-r--r--vmime/security/sasl/SASLSession.hpp150
-rw-r--r--vmime/security/sasl/SASLSocket.hpp (renamed from vmime/net/authenticationInfos.hpp)56
-rw-r--r--vmime/security/sasl/builtinSASLMechanism.hpp82
-rw-r--r--vmime/security/sasl/defaultSASLAuthenticator.hpp80
-rw-r--r--vmime/types.hpp2
-rw-r--r--vmime/utility/stream.hpp23
-rw-r--r--vmime/vmime.hpp18
55 files changed, 3530 insertions, 770 deletions
diff --git a/ChangeLog b/ChangeLog
index 317443b7..bcdea3a3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,11 @@
VERSION 0.7.2cvs
================
+2005-09-17 Vincent Richard <[email protected]>
+
+ * Added SASL support, based on GNU SASL library. Slightly modified
+ auhenticator object; see 'example6' which has been updated.
+
2005-09-06 Vincent Richard <[email protected]>
* Created 'vmime::security' and 'vmime::security::digest' namespaces.
diff --git a/SConstruct b/SConstruct
index b4ff328d..0f24b6e8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -170,12 +170,26 @@ libvmime_sources = [
# =============================== Misc ===============================
'misc/importanceHelper.cpp', 'misc/importanceHelper.hpp',
# ============================= Security =============================
+ 'security/authenticator.hpp',
+ 'security/defaultAuthenticator.cpp', 'security/defaultAuthenticator.hpp',
+ # -- digest
'security/digest/messageDigest.cpp', 'security/digest/messageDigest.hpp',
'security/digest/messageDigestFactory.cpp', 'security/digest/messageDigestFactory.hpp',
'security/digest/md5/md5MessageDigest.cpp', 'security/digest/md5/md5MessageDigest.hpp',
'security/digest/sha1/sha1MessageDigest.cpp', 'security/digest/sha1/sha1MessageDigest.hpp'
]
+libvmime_security_sasl_sources = [
+ 'security/sasl/SASLContext.cpp', 'security/sasl/SASLContext.hpp',
+ 'security/sasl/SASLSession.cpp', 'security/sasl/SASLSession.hpp',
+ 'security/sasl/SASLMechanism.hpp',
+ 'security/sasl/SASLMechanismFactory.cpp', 'security/sasl/SASLMechanismFactory.hpp',
+ 'security/sasl/SASLSocket.cpp', 'security/sasl/SASLSocket.hpp',
+ 'security/sasl/SASLAuthenticator.hpp',
+ 'security/sasl/defaultSASLAuthenticator.cpp', 'security/sasl/defaultSASLAuthenticator.hpp',
+ 'security/sasl/builtinSASLMechanism.cpp', 'security/sasl/builtinSASLMechanism.hpp'
+]
+
libvmime_examples_sources = [
'examples/README',
# 'examples/Makefile.am', # not generated
@@ -190,11 +204,8 @@ libvmime_examples_sources = [
]
libvmime_messaging_sources = [
- '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',
@@ -202,7 +213,6 @@ libvmime_messaging_sources = [
'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',
@@ -357,7 +367,7 @@ libvmime_autotools = [
'vmime/Makefile.in'
]
-libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources
+libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources + libvmime_security_sasl_sources
for i in range(len(libvmime_all_sources)):
f = libvmime_all_sources[i]
@@ -463,6 +473,14 @@ opts.AddOptions(
+ 'Currently available platform handlers: posix.',
'"posix"'
),
+ EnumOption(
+ 'with_sasl',
+ 'Enable SASL support (requires GNU SASL library)',
+ 'yes',
+ allowed_values = ('yes', 'no'),
+ map = { },
+ ignorecase = 1
+ ),
(
'sendmail_path',
'Specifies the path to sendmail.',
@@ -554,6 +572,8 @@ else:
#env.Append(LIBS = ['additional-lib-here'])
+env.ParseConfig('pkg-config --cflags --libs libgsasl')
+
# Generate help text for command line options
Help(opts.GenerateHelpText(env))
@@ -642,6 +662,7 @@ if env['with_messaging'] == 'yes':
print " * protocols : " + env['with_messaging_protocols']
print "File-system support : " + env['with_filesystem']
print "Platform handlers : " + env['with_platforms']
+print "SASL support : " + env['with_sasl']
if IsProtocolSupported(messaging_protocols, 'sendmail'):
print "Sendmail path : " + env['sendmail_path']
@@ -727,6 +748,12 @@ if env['with_filesystem'] == 'yes':
else:
config_hpp.write('#define VMIME_HAVE_FILESYSTEM_FEATURES 0\n')
+config_hpp.write('// -- SASL support\n')
+if env['with_sasl'] == 'yes':
+ config_hpp.write('#define VMIME_HAVE_SASL_SUPPORT 1\n')
+else:
+ config_hpp.write('#define VMIME_HAVE_SASL_SUPPORT 0\n')
+
config_hpp.write('// -- Messaging support\n')
if env['with_messaging'] == 'yes':
config_hpp.write('#define VMIME_HAVE_MESSAGING_FEATURES 1\n')
@@ -800,6 +827,11 @@ if env['with_messaging'] == 'yes':
for file in protosrc[1]:
libvmime_sel_sources.append(file)
+# -- SASL support
+if env['with_sasl'] == 'yes':
+ for file in libvmime_security_sasl_sources:
+ libvmime_sel_sources.append(file)
+
# -- platform handlers
for platform in platforms:
files = libvmime_platforms_sources[platform]
@@ -852,12 +884,13 @@ Default(libVmime)
# Tests
if env['build_tests'] == 'yes':
if env['debug'] == 'yes':
+ env = env.Copy()
+ env.Append(LIBS = ['cppunit', 'dl', packageVersionedGenericName + '-debug'])
+ env.Append(LIBPATH=['.'])
Default(
env.Program(
target = 'run-tests',
- source = libvmimetest_sources,
- LIBS=['cppunit', 'dl', packageVersionedGenericName + '-debug'],
- LIBPATH=['.']
+ source = libvmimetest_sources
)
)
else:
@@ -888,6 +921,13 @@ env.Install(includeDir, 'vmime/config.hpp')
# Pkg-config support
vmime_pc = open(packageVersionedGenericName + ".pc", 'w')
+vmime_pc_requires = ''
+vmime_pc_libs = ''
+
+if env['with_sasl'] == 'yes':
+ vmime_pc_requires = vmime_pc_requires + "libgsasl "
+ vmime_pc_libs = vmime_pc_libs + "-lgsasl "
+
vmime_pc.write("prefix=" + env['prefix'] + "\n")
vmime_pc.write("exec_prefix=" + env['prefix'] + "\n")
vmime_pc.write("libdir=" + env['prefix'] + "/lib\n")
@@ -896,8 +936,8 @@ vmime_pc.write("\n")
vmime_pc.write("Name: " + packageRealName + "\n")
vmime_pc.write("Description: " + packageDescription + "\n")
vmime_pc.write("Version: " + packageVersion + "\n")
-vmime_pc.write("Requires:\n")
-vmime_pc.write("Libs: -L${libdir} -l" + packageVersionedGenericName + "\n")
+vmime_pc.write("Requires: " + vmime_pc_requires + "\n")
+vmime_pc.write("Libs: -L${libdir} -l" + packageVersionedGenericName + " " + vmime_pc_libs + "\n")
#vmime_pc.write("Cflags: -I${includedir}/" + packageVersionedGenericName + "\n")
vmime_pc.write("Cflags: -I${includedir}/" + "\n")
@@ -969,8 +1009,8 @@ def generateAutotools(target, source, env):
vmime_pc_in.write("Name: @GENERIC_LIBRARY_NAME@\n")
vmime_pc_in.write("Description: " + packageDescription + "\n")
vmime_pc_in.write("Version: @VERSION@\n")
- vmime_pc_in.write("Requires:\n")
- vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@\n")
+ vmime_pc_in.write("Requires: @GSASL_REQUIRED@\n")
+ vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@\n")
#vmime_pc_in.write("Cflags: -I${includedir}/@GENERIC_VERSIONED_LIBRARY_NAME@\n")
vmime_pc_in.write("Cflags: -I${includedir}/\n")
vmime_pc_in.close()
@@ -1074,6 +1114,15 @@ INCLUDES = -I$(top_srcdir) -I$(srcdir) @PKGCONFIG_CFLAGS@ @EXTRA_CFLAGS@
Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n")
Makefile_am.write("endif\n")
+ # -- SASL support
+ x = selectFilesFromSuffixNot(libvmime_security_sasl_sources, '.hpp')
+ sourceFiles += x
+
+ Makefile_am.write("\n")
+ Makefile_am.write("if VMIME_HAVE_SASL_SUPPORT\n")
+ Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n")
+ Makefile_am.write("endif\n")
+
# -- platform handlers
for platform in libvmime_platforms_sources:
Makefile_am.write("\n")
@@ -1200,6 +1249,13 @@ else
AC_ERROR(no usable version of iconv has been found)
fi
+# -- GNU SASL Library (http://www.gnu.org/software/gsasl/)
+AC_CHECK_HEADER(gsasl.h,
+ AC_CHECK_LIB(gsasl, gsasl_check_version,
+ [have_gsasl=yes AC_SUBST(GSASL_AVAIL_LIBS, -lgsasl) AC_SUBST(GSASL_AVAIL_REQUIRED, libgsasl)],
+ have_gsasl=no),
+ have_gsasl=no)
+
# -- global constructors (stolen from 'configure.in' in libsigc++)
AC_MSG_CHECKING([if linker supports global constructors])
cat > mylib.$ac_ext <<EOF
@@ -1391,7 +1447,7 @@ esac
# ** debug
AC_ARG_ENABLE(debug,
- [ --enable-debug Turn on debugging (default: disabled)],
+ AC_HELP_STRING([--enable-debug], [Turn on debugging, default: disabled]),
[case "${enableval}" in
yes) conf_debug=yes ;;
no) conf_debug=no ;;
@@ -1410,8 +1466,7 @@ fi
# ** messaging
AC_ARG_ENABLE(messaging,
- [ --enable-messaging Enable messaging support\
- (connection to mail servers, default: enabled)],
+ AC_HELP_STRING([--enable-messaging], [Enable messaging support and connection to mail servers, default: enabled]),
[case "${enableval}" in
yes) conf_messaging=yes ;;
no) conf_messaging=no ;;
@@ -1427,6 +1482,38 @@ else
VMIME_HAVE_MESSAGING_FEATURES=0
fi
+# ** SASL
+
+AC_ARG_ENABLE(sasl,
+ AC_HELP_STRING([--enable-sasl], [Enable SASL support with GNU SASL, default: enabled]),
+ [case "${enableval}" in
+ yes) conf_sasl=yes ;;
+ no) conf_sasl=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-sasl) ;;
+ esac],
+ [conf_sasl=yes])
+
+if test "x$conf_sasl" = "xyes"; then
+ if test "x$have_gsasl" = "xyes"; then
+ AM_CONDITIONAL(VMIME_HAVE_SASL_SUPPORT, true)
+ VMIME_HAVE_SASL_SUPPORT=1
+
+ GSASL_REQUIRED=${GSASL_AVAIL_REQUIRED}
+ GSASL_LIBS=${GSASL_AVAIL_LIBS}
+ else
+ AC_MSG_ERROR(can't find an usable version of GNU SASL library)
+ fi
+else
+ AM_CONDITIONAL(VMIME_HAVE_SASL_SUPPORT, false)
+ VMIME_HAVE_SASL_SUPPORT=0
+
+ GSASL_REQUIRED=
+ GSASL_LIBS=
+fi
+
+AC_SUBST(GSASL_REQUIRED)
+AC_SUBST(GSASL_LIBS)
+
# ** platform handlers
VMIME_BUILTIN_PLATFORMS=''
@@ -1452,9 +1539,9 @@ VMIME_BUILTIN_MESSAGING_PROTOS=''
p = proto[0]
configure_in.write("AC_ARG_ENABLE(messaging-proto-" + p + ",\n")
- configure_in.write(" [ --enable-messaging-proto-" + p
- + " Enable built-in support for protocol '" + p + "' "
- + " (default: enabled)],\n")
+ configure_in.write(" AC_HELP_STRING([--enable-messaging-proto-" + p
+ + "], [Enable built-in support for protocol '" + p + "'"
+ + ", default: enabled]),\n")
configure_in.write(' [case "${enableval}" in\n')
configure_in.write(' yes) conf_messaging_proto_' + p + '=yes ;;\n')
configure_in.write(' no) conf_messaging_proto_' + p + '=no ;;\n')
@@ -1514,9 +1601,9 @@ fi
configure_in.write('fi\n\n')
configure_in.write("AC_ARG_ENABLE(platform-" + p + ",\n")
- configure_in.write(" [ --enable-platform-" + p
- + " Compile built-in platform handler for '" + p + "' "
- + " (default: disabled, except if default for your platform)],\n")
+ configure_in.write(" AC_HELP_STRING([--enable-platform-" + p
+ + "], [Compile built-in platform handler for '" + p + "' "
+ + ", default: disabled, except if default for your platform]),\n")
configure_in.write(' [case "${enableval}" in\n')
configure_in.write(' yes) conf_platform_' + p + '=yes ;;\n')
configure_in.write(' no) conf_platform_' + p + '=no ;;\n')
@@ -1551,7 +1638,7 @@ AC_SUBST(PKGCONFIG_CFLAGS)
AC_SUBST(PKGCONFIG_LIBS)
EXTRA_CFLAGS="$EXTRA_CFLAGS -D_REENTRANT=1"
-EXTRA_LIBS=""
+EXTRA_LIBS="$GSASL_LIBS"
CFLAGS=""
CXXFLAGS=""
@@ -1666,6 +1753,8 @@ typedef unsigned ${VMIME_TYPE_INT32} vmime_uint32;
#define VMIME_WIDE_CHAR_SUPPORT 0
// -- File-system support
#define VMIME_HAVE_FILESYSTEM_FEATURES 1
+// -- SASL support
+#define VMIME_HAVE_SASL_SUPPORT ${VMIME_HAVE_SASL_SUPPORT}
// -- Messaging support
#define VMIME_HAVE_MESSAGING_FEATURES ${VMIME_HAVE_MESSAGING_FEATURES}
""")
@@ -1719,6 +1808,9 @@ Messaging support : $conf_messaging
* protocols :$VMIME_BUILTIN_MESSAGING_PROTOS
File-system support : yes
Platform handlers :$VMIME_BUILTIN_PLATFORMS
+SASL support : $conf_sasl
+
+Please check 'vmime/config.hpp' to ensure the configuration is correct.
])
""")
diff --git a/examples/example6.cpp b/examples/example6.cpp
index 97102e18..fa3ee748 100644
--- a/examples/example6.cpp
+++ b/examples/example6.cpp
@@ -20,6 +20,7 @@
#include <iostream>
#include <sstream>
#include <vector>
+#include <map>
#include "vmime/vmime.hpp"
#include "vmime/platforms/posix/posixHandler.hpp"
@@ -31,27 +32,65 @@ static vmime::ref <vmime::net::session> g_session
// Authentification handler
-class interactiveAuthenticator : public vmime::net::authenticator
+class interactiveAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator
{
- const vmime::net::authenticationInfos requestAuthInfos() const
+ const std::vector <vmime::ref <vmime::security::sasl::SASLMechanism> > getAcceptableMechanisms
+ (const std::vector <vmime::ref <vmime::security::sasl::SASLMechanism> >& available,
+ vmime::ref <vmime::security::sasl::SASLMechanism> suggested) const
{
- vmime::string username, password;
+ std::cout << std::endl << "Available SASL mechanisms:" << std::endl;
- std::cout << std::endl;
- std::cout << "Please authenticate yourself:" << std::endl;
+ for (unsigned int i = 0 ; i < available.size() ; ++i)
+ {
+ std::cout << " " << available[i]->getName();
- std::cout << " Username: ";
- std::cout.flush();
+ if (suggested && available[i]->getName() == suggested->getName())
+ std::cout << "(suggested)";
+ }
+
+ std::cout << std::endl << std::endl;
- std::getline(std::cin, username);
+ return defaultSASLAuthenticator::getAcceptableMechanisms(available, suggested);
+ }
- std::cout << " Password: ";
+ void setSASLMechanism(vmime::ref <vmime::security::sasl::SASLMechanism> mech)
+ {
+ std::cout << "Trying " << mech->getName() << std::endl;
+
+ defaultSASLAuthenticator::setSASLMechanism(mech);
+ }
+
+ const vmime::string getUsername() const
+ {
+ if (m_username.empty())
+ m_username = getUserInput("Username");
+
+ return m_username;
+ }
+
+ const vmime::string getPassword() const
+ {
+ if (m_password.empty())
+ m_password = getUserInput("Password");
+
+ return m_password;
+ }
+
+ static const vmime::string getUserInput(const std::string& prompt)
+ {
+ std::cout << prompt << ": ";
std::cout.flush();
- std::getline(std::cin, password);
+ vmime::string res;
+ std::getline(std::cin, res);
- return (vmime::net::authenticationInfos(username, password));
+ return res;
}
+
+private:
+
+ mutable vmime::string m_username;
+ mutable vmime::string m_password;
};
@@ -215,7 +254,7 @@ static void sendMessage()
// You can also set some properties (see example7 to know the properties
// available for each service). For example, for SMTP:
-// tr->setProperty("options.need-authentication", true);
+ tr->setProperty("options.need-authentication", true);
// Information about the mail
std::cout << "Enter email of the expeditor (eg. [email protected]): ";
diff --git a/src/exception.cpp b/src/exception.cpp
index 7482482a..4fbd4060 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -347,8 +347,7 @@ const char* net_exception::name() const throw() { return "net_exception"; }
socket_exception::~socket_exception() throw() {}
socket_exception::socket_exception(const string& what, const exception& other)
: net_exception(what.empty()
- ? "Socket error."
- : "Socket error: '" + what + "'.", other) {}
+ ? "Socket error." : what, other) {}
exception* socket_exception::clone() const { return new socket_exception(*this); }
const char* socket_exception::name() const throw() { return "socket_exception"; }
@@ -361,8 +360,7 @@ const char* socket_exception::name() const throw() { return "socket_exception";
connection_error::~connection_error() throw() {}
connection_error::connection_error(const string& what, const exception& other)
: socket_exception(what.empty()
- ? "Connection error."
- : "Connection error: '" + what + "'.", other) {}
+ ? "Connection error." : what, other) {}
exception* connection_error::clone() const { return new connection_error(*this); }
const char* connection_error::name() const throw() { return "connection_error"; }
@@ -675,6 +673,48 @@ const char* file_not_found::name() const throw() { return "file_not_found"; }
#endif // VMIME_HAVE_FILESYSTEM_FEATURES
+#if VMIME_HAVE_SASL_SUPPORT
+
+
+//
+// sasl_exception
+//
+
+sasl_exception::~sasl_exception() throw() {}
+sasl_exception::sasl_exception(const string& what, const exception& other)
+ : exception(what, other) {}
+
+exception* sasl_exception::clone() const { return new sasl_exception(*this); }
+const char* sasl_exception::name() const throw() { return "sasl_exception"; }
+
+
+//
+// no_such_mechanism
+//
+
+no_such_mechanism::~no_such_mechanism() throw() {}
+no_such_mechanism::no_such_mechanism(const string& name, const exception& other)
+ : sasl_exception("No such SASL mechanism: '" + name + "'.", other) {}
+
+exception* no_such_mechanism::clone() const { return new no_such_mechanism(*this); }
+const char* no_such_mechanism::name() const throw() { return "no_such_mechanism"; }
+
+
+//
+// no_auth_information
+//
+
+no_auth_information::~no_auth_information() throw() {}
+no_auth_information::no_auth_information(const exception& other)
+ : sasl_exception("Information cannot be provided.", other) {}
+
+exception* no_auth_information::clone() const { return new no_auth_information(*this); }
+const char* no_auth_information::name() const throw() { return "no_auth_information"; }
+
+
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
} // exceptions
diff --git a/src/net/authenticationInfos.cpp b/src/net/authenticationInfos.cpp
deleted file mode 100644
index e5a93fb2..00000000
--- a/src/net/authenticationInfos.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// 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
deleted file mode 100644
index d894915c..00000000
--- a/src/net/authenticator.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// 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/defaultAuthenticator.cpp b/src/net/defaultAuthenticator.cpp
deleted file mode 100644
index 59835b64..00000000
--- a/src/net/defaultAuthenticator.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// 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/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp
index 5191dc94..02b6c607 100644
--- a/src/net/imap/IMAPConnection.cpp
+++ b/src/net/imap/IMAPConnection.cpp
@@ -25,6 +25,10 @@
#include "vmime/exception.hpp"
#include "vmime/platformDependant.hpp"
+#if VMIME_HAVE_SASL_SUPPORT
+ #include "vmime/security/sasl/SASLContext.hpp"
+#endif // VMIME_HAVE_SASL_SUPPORT
+
#include <sstream>
@@ -42,7 +46,7 @@ namespace net {
namespace imap {
-IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth)
+IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <security::authenticator> auth)
: m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL),
m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL)
{
@@ -51,10 +55,17 @@ IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> a
IMAPConnection::~IMAPConnection()
{
- if (isConnected())
- disconnect();
- else if (m_socket)
- internalDisconnect();
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
}
@@ -107,33 +118,294 @@ void IMAPConnection::connect()
}
else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH)
{
- const authenticationInfos auth = m_auth->requestAuthInfos();
+ try
+ {
+ authenticate();
+ }
+ catch (...)
+ {
+ m_state = STATE_NONE;
+ throw;
+ }
+ }
+
+ // Get the hierarchy separator character
+ initHierarchySeparator();
- // TODO: other authentication methods
+ // Switch to state "Authenticated"
+ setState(STATE_AUTHENTICATED);
+}
- send(true, "LOGIN " + IMAPUtils::quoteString(auth.getUsername())
- + " " + IMAPUtils::quoteString(auth.getPassword()), true);
- utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+void IMAPConnection::authenticate()
+{
+ getAuthenticator()->setService(thisRef().dynamicCast <service>());
- if (resp->isBad())
+#if VMIME_HAVE_SASL_SUPPORT
+ // First, try SASL authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
+ {
+ try
{
- internalDisconnect();
- throw exceptions::command_error("LOGIN", m_parser->lastLine());
+ authenticateSASL();
+ return;
+ }
+ catch (exceptions::authentication_error& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try normal authentication
+ }
}
- else if (resp->response_done()->response_tagged()->
- resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ catch (exception& e)
{
internalDisconnect();
- throw exceptions::authentication_error(m_parser->lastLine());
+ throw e;
}
}
+#endif // VMIME_HAVE_SASL_SUPPORT
- // Get the hierarchy separator character
- initHierarchySeparator();
+ // Normal authentication
+ const string username = getAuthenticator()->getUsername();
+ const string password = getAuthenticator()->getPassword();
- // Switch to state "Authenticated"
- setState(STATE_AUTHENTICATED);
+ send(true, "LOGIN " + IMAPUtils::quoteString(username)
+ + " " + IMAPUtils::quoteString(password), 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());
+ }
+}
+
+
+#if VMIME_HAVE_SASL_SUPPORT
+
+void IMAPConnection::authenticateSASL()
+{
+ if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
+ throw exceptions::authentication_error("No SASL authenticator available.");
+
+ const std::vector <string> capa = getCapabilities();
+ std::vector <string> saslMechs;
+
+ for (unsigned int i = 0 ; i < capa.size() ; ++i)
+ {
+ const string& x = capa[i];
+
+ if (x.length() > 5 &&
+ (x[0] == 'A' || x[0] == 'a') &&
+ (x[1] == 'U' || x[1] == 'u') &&
+ (x[2] == 'T' || x[2] == 't') &&
+ (x[3] == 'H' || x[3] == 'h') &&
+ x[4] == '=')
+ {
+ saslMechs.push_back(string(x.begin() + 5, x.end()));
+ }
+ }
+
+ if (saslMechs.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ std::vector <ref <security::sasl::SASLMechanism> > mechList;
+
+ ref <security::sasl::SASLContext> saslContext =
+ vmime::create <security::sasl::SASLContext>();
+
+ for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
+ {
+ try
+ {
+ mechList.push_back
+ (saslContext->createMechanism(saslMechs[i]));
+ }
+ catch (exceptions::no_such_mechanism&)
+ {
+ // Ignore mechanism
+ }
+ }
+
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ // Try to suggest a mechanism among all those supported
+ ref <security::sasl::SASLMechanism> suggestedMech =
+ saslContext->suggestMechanism(mechList);
+
+ if (!suggestedMech)
+ throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
+
+ // Allow application to choose which mechanisms to use
+ mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
+ getAcceptableMechanisms(mechList, suggestedMech);
+
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ // Try each mechanism in the list in turn
+ for (unsigned int i = 0 ; i < mechList.size() ; ++i)
+ {
+ ref <security::sasl::SASLMechanism> mech = mechList[i];
+
+ ref <security::sasl::SASLSession> saslSession =
+ saslContext->createSession("imap", getAuthenticator(), mech);
+
+ saslSession->init();
+
+ send(true, "AUTHENTICATE " + mech->getName(), true);
+
+ for (bool cont = true ; cont ; )
+ {
+ utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ if (resp->response_done() &&
+ resp->response_done()->response_tagged() &&
+ resp->response_done()->response_tagged()->resp_cond_state()->
+ status() == IMAPParser::resp_cond_state::OK)
+ {
+ m_socket = saslSession->getSecuredSocket(m_socket);
+ return;
+ }
+ else
+ {
+ std::vector <IMAPParser::continue_req_or_response_data*>
+ respDataList = resp->continue_req_or_response_data();
+
+ string response;
+
+ for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
+ {
+ if (respDataList[i]->continue_req())
+ {
+ response = respDataList[i]->continue_req()->resp_text()->text();
+ break;
+ }
+ }
+
+ if (response.empty())
+ {
+ cont = false;
+ continue;
+ }
+
+ byte* challenge = 0;
+ int challengeLen = 0;
+
+ byte* resp = 0;
+ int respLen = 0;
+
+ try
+ {
+ // Extract challenge
+ saslContext->decodeB64(response, &challenge, &challengeLen);
+
+ // Prepare response
+ saslSession->evaluateChallenge
+ (challenge, challengeLen, &resp, &respLen);
+
+ // Send response
+ send(false, saslContext->encodeB64(resp, respLen), true);
+ }
+ catch (exceptions::sasl_exception& e)
+ {
+ if (challenge)
+ {
+ delete [] challenge;
+ challenge = NULL;
+ }
+
+ if (resp)
+ {
+ delete [] resp;
+ resp = NULL;
+ }
+
+ // Cancel SASL exchange
+ send(false, "*", true);
+ }
+ catch (...)
+ {
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+
+ throw;
+ }
+
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+ }
+ }
+ }
+
+ throw exceptions::authentication_error
+ ("Could not authenticate using SASL: all mechanisms failed.");
+}
+
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
+const std::vector <string> IMAPConnection::getCapabilities()
+{
+ send(true, "CAPABILITY", true);
+
+ utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ std::vector <string> res;
+
+ if (resp->response_done()->response_tagged()->
+ resp_cond_state()->status() == IMAPParser::resp_cond_state::OK)
+ {
+ const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
+ resp->continue_req_or_response_data();
+
+ for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
+ {
+ if (respDataList[i]->response_data() == NULL)
+ continue;
+
+ const IMAPParser::capability_data* capaData =
+ respDataList[i]->response_data()->capability_data();
+
+ std::vector <IMAPParser::capability*> caps = capaData->capabilities();
+
+ for (unsigned int j = 0 ; j < caps.size() ; ++j)
+ {
+ if (caps[j]->auth_type())
+ res.push_back("AUTH=" + caps[j]->auth_type()->name());
+ else
+ res.push_back(caps[j]->atom()->value());
+ }
+ }
+ }
+
+ return res;
+}
+
+
+ref <security::authenticator> IMAPConnection::getAuthenticator()
+{
+ return m_auth;
}
diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp
index ea0ddfd9..a0df8f6b 100644
--- a/src/net/imap/IMAPFolder.cpp
+++ b/src/net/imap/IMAPFolder.cpp
@@ -133,7 +133,7 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable)
// Open a connection for this folder
ref <IMAPConnection> connection =
- vmime::create <IMAPConnection>(m_store, m_store->oneTimeAuthenticator());
+ vmime::create <IMAPConnection>(m_store, m_store->getAuthenticator());
try
{
diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp
index c8dab178..b7a9cbed 100644
--- a/src/net/imap/IMAPStore.cpp
+++ b/src/net/imap/IMAPStore.cpp
@@ -32,67 +32,23 @@ 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
+IMAPStore::IMAPStore(ref <session> sess, ref <security::authenticator> auth)
+ : store(sess, getInfosInstance(), auth), m_connection(NULL)
{
-public:
+}
- IMAPauthenticator(ref <authenticator> auth)
- : m_auth(auth), m_infos(NULL)
- {
- }
- ~IMAPauthenticator()
+IMAPStore::~IMAPStore()
+{
+ try
{
+ if (isConnected())
+ disconnect();
}
-
- const authenticationInfos requestAuthInfos() const
+ catch (vmime::exception&)
{
- if (m_infos == NULL)
- m_infos = vmime::create <authenticationInfos>(m_auth->requestAuthInfos());
-
- return (*m_infos);
+ // Ignore
}
-
-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);
}
@@ -140,10 +96,8 @@ 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);
+ (thisWeakRef().dynamicCast <IMAPStore>(), getAuthenticator());
try
{
@@ -179,8 +133,6 @@ void IMAPStore::disconnect()
m_connection->disconnect();
- m_oneTimeAuth = NULL;
-
m_connection = NULL;
}
@@ -263,7 +215,10 @@ const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const
static props p =
{
// IMAP-specific options
- // (none)
+#if VMIME_HAVE_SASL_SUPPORT
+ property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
+ property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"),
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
@@ -286,7 +241,10 @@ const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProper
const props& p = getProperties();
// IMAP-specific options
- // (none)
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);
diff --git a/src/net/maildir/maildirStore.cpp b/src/net/maildir/maildirStore.cpp
index 731024fa..5ddc0897 100644
--- a/src/net/maildir/maildirStore.cpp
+++ b/src/net/maildir/maildirStore.cpp
@@ -39,7 +39,7 @@ namespace net {
namespace maildir {
-maildirStore::maildirStore(ref <session> sess, ref <authenticator> auth)
+maildirStore::maildirStore(ref <session> sess, ref <security::authenticator> auth)
: store(sess, getInfosInstance(), auth), m_connected(false)
{
}
@@ -47,8 +47,15 @@ maildirStore::maildirStore(ref <session> sess, ref <authenticator> auth)
maildirStore::~maildirStore()
{
- if (isConnected())
- disconnect();
+ try
+ {
+ if (isConnected())
+ disconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
}
@@ -96,7 +103,7 @@ const bool maildirStore::isValidFolderName(const folder::path::component& name)
const string& buf = name.getBuffer();
// Name cannot start/end with spaces
- if (utility::stringUtils::trim(buf) != name.getBuffer())
+ if (utility::stringUtils::trim(buf) != buf)
return false;
// Name cannot start with '.'
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp
index 5041d2d3..65973c8f 100644
--- a/src/net/pop3/POP3Store.cpp
+++ b/src/net/pop3/POP3Store.cpp
@@ -25,6 +25,11 @@
#include "vmime/messageId.hpp"
#include "vmime/security/digest/messageDigestFactory.hpp"
#include "vmime/utility/filteredStream.hpp"
+#include "vmime/utility/stringUtils.hpp"
+
+#if VMIME_HAVE_SASL_SUPPORT
+ #include "vmime/security/sasl/SASLContext.hpp"
+#endif // VMIME_HAVE_SASL_SUPPORT
#include <algorithm>
@@ -41,7 +46,7 @@ namespace net {
namespace pop3 {
-POP3Store::POP3Store(ref <session> sess, ref <authenticator> auth)
+POP3Store::POP3Store(ref <session> sess, ref <security::authenticator> auth)
: store(sess, getInfosInstance(), auth), m_socket(NULL),
m_authentified(false), m_timeoutHandler(NULL)
{
@@ -50,10 +55,17 @@ POP3Store::POP3Store(ref <session> sess, ref <authenticator> auth)
POP3Store::~POP3Store()
{
- if (isConnected())
- disconnect();
- else if (m_socket)
- internalDisconnect();
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
}
@@ -128,98 +140,319 @@ void POP3Store::connect()
string response;
readResponse(response, false);
- if (isSuccessResponse(response))
+ if (!isSuccessResponse(response))
{
- bool authentified = false;
-
- const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
+ internalDisconnect();
+ throw exceptions::connection_greeting_error(response);
+ }
- // Secured authentication with APOP (if requested and if available)
- //
- // eg: C: APOP vincent <digest>
- // --- S: +OK vincent is a valid mailbox
- messageId mid(response);
+ // Start authentication process
+ authenticate(messageId(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"
- ref <security::digest::messageDigest> md5 =
- security::digest::messageDigestFactory::getInstance()->create("md5");
- md5->update(mid.generate() + auth.getPassword());
- md5->finalize();
+void POP3Store::authenticate(const messageId& randomMID)
+{
+ getAuthenticator()->setService(thisRef().dynamicCast <service>());
- sendRequest("APOP " + auth.getUsername() + " " + md5->getHexDigest());
- readResponse(response, false);
+#if VMIME_HAVE_SASL_SUPPORT
+ // First, try SASL authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
+ {
+ try
+ {
+ authenticateSASL();
- if (isSuccessResponse(response))
- {
- authentified = true;
- }
- else
- {
- if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
- {
- internalDisconnect();
- throw exceptions::authentication_error(response);
- }
- }
+ m_authentified = true;
+ return;
+ }
+ catch (exceptions::authentication_error& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on APOP/normal authentication
+ internalDisconnect();
+ throw e;
}
else
{
- // APOP not supported
- if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
- {
- // Can't fallback on basic authentification
- internalDisconnect();
- throw exceptions::unsupported_option();
- }
+ // Ignore, will try APOP/normal authentication
}
}
+ catch (exception& e)
+ {
+ internalDisconnect();
+ throw e;
+ }
+ }
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ // Secured authentication with APOP (if requested and if available)
+ //
+ // eg: C: APOP vincent <digest>
+ // --- S: +OK vincent is a valid mailbox
+
+ const string username = getAuthenticator()->getUsername();
+ const string password = getAuthenticator()->getPassword();
- if (!authentified)
+ string response;
+
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
+ {
+ if (randomMID.getLeft().length() != 0 &&
+ randomMID.getRight().length() != 0)
{
- // 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());
+ // <digest> is the result of MD5 applied to "<message-id>password"
+ ref <security::digest::messageDigest> md5 =
+ security::digest::messageDigestFactory::getInstance()->create("md5");
+
+ md5->update(randomMID.generate() + password);
+ md5->finalize();
+
+ sendRequest("APOP " + username + " " + md5->getHexDigest());
readResponse(response, false);
if (isSuccessResponse(response))
{
- sendRequest("PASS " + auth.getPassword());
- readResponse(response, false);
+ m_authentified = true;
+ return;
+ }
+ else
+ {
+ // Some servers close the connection after an
+ // unsuccessful APOP command, so the fallback
+ // may not always work...
+ //
+ // S: +OK Qpopper (version 4.0.5) at xxx starting. <30396.1126730747@xxx>
+ // C: APOP plop c5e0a87d088ec71d60e32692d4c5bdf4
+ // S: -ERR [AUTH] Password supplied for "o" is incorrect.
+ // S: +OK Pop server at xxx signing off.
+ // [Connection closed by foreign host.]
- if (!isSuccessResponse(response))
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
+ // Can't fallback on basic authentication
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
- else
+ }
+ else
+ {
+ // APOP not supported
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
+ // Can't fallback on basic authentication
internalDisconnect();
- throw exceptions::authentication_error(response);
+ throw exceptions::authentication_error("APOP not supported");
}
}
}
- else
+
+ // 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 " + username);
+ readResponse(response, false);
+
+ if (!isSuccessResponse(response))
{
internalDisconnect();
- throw exceptions::connection_greeting_error(response);
+ throw exceptions::authentication_error(response);
+ }
+
+ sendRequest("PASS " + password);
+ readResponse(response, false);
+
+ if (!isSuccessResponse(response))
+ {
+ internalDisconnect();
+ throw exceptions::authentication_error(response);
}
m_authentified = true;
}
+#if VMIME_HAVE_SASL_SUPPORT
+
+void POP3Store::authenticateSASL()
+{
+ if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
+ throw exceptions::authentication_error("No SASL authenticator available.");
+
+ std::vector <string> capa = getCapabilities();
+ std::vector <string> saslMechs;
+
+ for (unsigned int i = 0 ; i < capa.size() ; ++i)
+ {
+ const string& x = capa[i];
+
+ // C: CAPA
+ // S: +OK List of capabilities follows
+ // S: LOGIN-DELAY 0
+ // S: PIPELINING
+ // S: UIDL
+ // S: ...
+ // S: SASL DIGEST-MD5 CRAM-MD5 <-----
+ // S: EXPIRE NEVER
+ // S: ...
+
+ if (x.length() > 5 &&
+ (x[0] == 'S' || x[0] == 's') &&
+ (x[1] == 'A' || x[1] == 'a') &&
+ (x[2] == 'S' || x[2] == 's') &&
+ (x[3] == 'L' || x[3] == 'l') &&
+ std::isspace(x[4]))
+ {
+ const string list(x.begin() + 5, x.end());
+
+ std::istringstream iss(list);
+ string mech;
+
+ while (iss >> mech)
+ saslMechs.push_back(mech);
+ }
+ }
+
+ if (saslMechs.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ std::vector <ref <security::sasl::SASLMechanism> > mechList;
+
+ ref <security::sasl::SASLContext> saslContext =
+ vmime::create <security::sasl::SASLContext>();
+
+ for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
+ {
+ try
+ {
+ mechList.push_back
+ (saslContext->createMechanism(saslMechs[i]));
+ }
+ catch (exceptions::no_such_mechanism&)
+ {
+ // Ignore mechanism
+ }
+ }
+
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ // Try to suggest a mechanism among all those supported
+ ref <security::sasl::SASLMechanism> suggestedMech =
+ saslContext->suggestMechanism(mechList);
+
+ if (!suggestedMech)
+ throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
+
+ // Allow application to choose which mechanisms to use
+ mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
+ getAcceptableMechanisms(mechList, suggestedMech);
+
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
+
+ // Try each mechanism in the list in turn
+ for (unsigned int i = 0 ; i < mechList.size() ; ++i)
+ {
+ ref <security::sasl::SASLMechanism> mech = mechList[i];
+
+ ref <security::sasl::SASLSession> saslSession =
+ saslContext->createSession("pop3", getAuthenticator(), mech);
+
+ saslSession->init();
+
+ sendRequest("AUTH " + mech->getName());
+
+ for (bool cont = true ; cont ; )
+ {
+ string response;
+ readResponse(response, false);
+
+ switch (getResponseCode(response))
+ {
+ case RESPONSE_OK:
+ {
+ m_socket = saslSession->getSecuredSocket(m_socket);
+ return;
+ }
+ case RESPONSE_READY:
+ {
+ byte* challenge = 0;
+ int challengeLen = 0;
+
+ byte* resp = 0;
+ int respLen = 0;
+
+ try
+ {
+ // Extract challenge
+ stripResponseCode(response, response);
+ saslContext->decodeB64(response, &challenge, &challengeLen);
+
+ // Prepare response
+ saslSession->evaluateChallenge
+ (challenge, challengeLen, &resp, &respLen);
+
+ // Send response
+ sendRequest(saslContext->encodeB64(resp, respLen));
+ }
+ catch (exceptions::sasl_exception& e)
+ {
+ if (challenge)
+ {
+ delete [] challenge;
+ challenge = NULL;
+ }
+
+ if (resp)
+ {
+ delete [] resp;
+ resp = NULL;
+ }
+
+ // Cancel SASL exchange
+ sendRequest("*");
+ }
+ catch (...)
+ {
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+
+ throw;
+ }
+
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+
+ break;
+ }
+ default:
+
+ cont = false;
+ break;
+ }
+ }
+ }
+
+ throw exceptions::authentication_error
+ ("Could not authenticate using SASL: all mechanisms failed.");
+}
+
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
const bool POP3Store::isConnected() const
{
return (m_socket && m_socket->isConnected() && m_authentified);
@@ -259,7 +492,7 @@ void POP3Store::internalDisconnect()
void POP3Store::noop()
{
- m_socket->send("NOOP");
+ sendRequest("NOOP");
string response;
readResponse(response, false);
@@ -269,12 +502,33 @@ void POP3Store::noop()
}
-const bool POP3Store::isSuccessResponse(const string& buffer)
+const std::vector <string> POP3Store::getCapabilities()
{
- static const string OK("+OK");
+ sendRequest("CAPA");
+
+ string response;
+ readResponse(response, true);
+
+ std::vector <string> res;
- return (buffer.length() >= 3 &&
- std::equal(buffer.begin(), buffer.begin() + 3, OK.begin()));
+ if (isSuccessResponse(response))
+ {
+ stripFirstLine(response, response);
+
+ std::istringstream iss(response);
+ string line;
+
+ while (std::getline(iss, line, '\n'))
+ res.push_back(utility::stringUtils::trim(line));
+ }
+
+ return res;
+}
+
+
+const bool POP3Store::isSuccessResponse(const string& buffer)
+{
+ return getResponseCode(buffer) == RESPONSE_OK;
}
@@ -296,6 +550,34 @@ const bool POP3Store::stripFirstLine(const string& buffer, string& result, strin
}
+const int POP3Store::getResponseCode(const string& buffer)
+{
+ if (buffer.length() >= 2)
+ {
+ // +[space]
+ if (buffer[0] == '+' &&
+ (buffer[1] == ' ' || buffer[1] == '\t'))
+ {
+ return RESPONSE_READY;
+ }
+
+ // +OK
+ if (buffer.length() >= 3)
+ {
+ if (buffer[0] == '+' &&
+ (buffer[1] == 'O' || buffer[1] == 'o') &&
+ (buffer[2] == 'K' || buffer[1] == 'k'))
+ {
+ return RESPONSE_OK;
+ }
+ }
+ }
+
+ // -ERR or whatever
+ return RESPONSE_ERR;
+}
+
+
void POP3Store::stripResponseCode(const string& buffer, string& result)
{
const string::size_type pos = buffer.find_first_of(" \t");
@@ -588,8 +870,12 @@ 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"),
+ property("options.apop", serviceInfos::property::TYPE_BOOL, "true"),
+ property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "true"),
+#if VMIME_HAVE_SASL_SUPPORT
+ property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
+ property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"),
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
@@ -614,6 +900,10 @@ const std::vector <serviceInfos::property> POP3Store::_infos::getAvailableProper
// POP3-specific options
list.push_back(p.PROPERTY_OPTIONS_APOP);
list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK);
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);
diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp
index 1b6edff9..b8b9f912 100644
--- a/src/net/sendmail/sendmailTransport.cpp
+++ b/src/net/sendmail/sendmailTransport.cpp
@@ -46,7 +46,7 @@ namespace net {
namespace sendmail {
-sendmailTransport::sendmailTransport(ref <session> sess, ref <authenticator> auth)
+sendmailTransport::sendmailTransport(ref <session> sess, ref <security::authenticator> auth)
: transport(sess, getInfosInstance(), auth), m_connected(false)
{
}
@@ -54,8 +54,15 @@ sendmailTransport::sendmailTransport(ref <session> sess, ref <authenticator> aut
sendmailTransport::~sendmailTransport()
{
- if (isConnected())
- disconnect();
+ try
+ {
+ if (isConnected())
+ disconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
}
diff --git a/src/net/service.cpp b/src/net/service.cpp
index 9cf59d84..018bae48 100644
--- a/src/net/service.cpp
+++ b/src/net/service.cpp
@@ -17,22 +17,33 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
+#include "vmime/config.hpp"
#include "vmime/net/service.hpp"
-#include "vmime/net/defaultAuthenticator.hpp"
+#if VMIME_HAVE_SASL_SUPPORT
+ #include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
+#else
+ #include "vmime/security/defaultAuthenticator.hpp"
+#endif // VMIME_HAVE_SASL_SUPPORT
namespace vmime {
namespace net {
-service::service(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+service::service(ref <session> sess, const serviceInfos& /* infos */,
+ ref <security::authenticator> auth)
: m_session(sess), m_auth(auth)
{
if (!auth)
{
- m_auth = vmime::create <defaultAuthenticator>
- (sess, infos.getPropertyPrefix());
+#if VMIME_HAVE_SASL_SUPPORT
+ m_auth = vmime::create
+ <security::sasl::defaultSASLAuthenticator>();
+#else
+ m_auth = vmime::create
+ <security::defaultAuthenticator>();
+#endif // VMIME_HAVE_SASL_SUPPORT
}
}
@@ -54,17 +65,23 @@ ref <session> service::getSession()
}
-ref <const authenticator> service::getAuthenticator() const
+ref <const security::authenticator> service::getAuthenticator() const
{
return (m_auth);
}
-ref <authenticator> service::getAuthenticator()
+ref <security::authenticator> service::getAuthenticator()
{
return (m_auth);
}
+void service::setAuthenticator(ref <security::authenticator> auth)
+{
+ m_auth = auth;
+}
+
+
} // net
} // vmime
diff --git a/src/net/serviceFactory.cpp b/src/net/serviceFactory.cpp
index 43697cc8..f4b39925 100644
--- a/src/net/serviceFactory.cpp
+++ b/src/net/serviceFactory.cpp
@@ -48,14 +48,16 @@ serviceFactory* serviceFactory::getInstance()
ref <service> serviceFactory::create
- (ref <session> sess, const string& protocol, ref <authenticator> auth)
+ (ref <session> sess, const string& protocol,
+ ref <security::authenticator> auth)
{
return (getServiceByProtocol(protocol)->create(sess, auth));
}
ref <service> serviceFactory::create
- (ref <session> sess, const utility::url& u, ref <authenticator> auth)
+ (ref <session> sess, const utility::url& u,
+ ref <security::authenticator> auth)
{
ref <service> serv = create(sess, u.getProtocol(), auth);
diff --git a/src/net/session.cpp b/src/net/session.cpp
index 2c46bf60..f6b0fdd1 100644
--- a/src/net/session.cpp
+++ b/src/net/session.cpp
@@ -50,13 +50,14 @@ session::~session()
}
-ref <transport> session::getTransport(ref <authenticator> auth)
+ref <transport> session::getTransport(ref <security::authenticator> auth)
{
return (getTransport(m_props["transport.protocol"], auth));
}
-ref <transport> session::getTransport(const string& protocol, ref <authenticator> auth)
+ref <transport> session::getTransport
+ (const string& protocol, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
@@ -68,7 +69,8 @@ ref <transport> session::getTransport(const string& protocol, ref <authenticator
}
-ref <transport> session::getTransport(const utility::url& url, ref <authenticator> auth)
+ref <transport> session::getTransport
+ (const utility::url& url, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);
@@ -80,13 +82,14 @@ ref <transport> session::getTransport(const utility::url& url, ref <authenticato
}
-ref <store> session::getStore(ref <authenticator> auth)
+ref <store> session::getStore(ref <security::authenticator> auth)
{
return (getStore(m_props["store.protocol"], auth));
}
-ref <store> session::getStore(const string& protocol, ref <authenticator> auth)
+ref <store> session::getStore
+ (const string& protocol, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
@@ -98,7 +101,8 @@ ref <store> session::getStore(const string& protocol, ref <authenticator> auth)
}
-ref <store> session::getStore(const utility::url& url, ref <authenticator> auth)
+ref <store> session::getStore
+ (const utility::url& url, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);
diff --git a/src/net/simpleAuthenticator.cpp b/src/net/simpleAuthenticator.cpp
deleted file mode 100644
index af125f77..00000000
--- a/src/net/simpleAuthenticator.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// 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::requestAuthInfos() 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
index 1cbffcf5..a5f034a6 100644
--- a/src/net/smtp/SMTPTransport.cpp
+++ b/src/net/smtp/SMTPTransport.cpp
@@ -27,6 +27,11 @@
#include "vmime/net/authHelper.hpp"
#include "vmime/utility/filteredStream.hpp"
+#include "vmime/utility/stringUtils.hpp"
+
+#if VMIME_HAVE_SASL_SUPPORT
+ #include "vmime/security/sasl/SASLContext.hpp"
+#endif // VMIME_HAVE_SASL_SUPPORT
// Helpers for service properties
@@ -41,7 +46,7 @@ namespace net {
namespace smtp {
-SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth)
+SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth)
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL)
{
@@ -50,10 +55,17 @@ SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth)
SMTPTransport::~SMTPTransport()
{
- if (isConnected())
- disconnect();
- else if (m_socket)
- internalDisconnect();
+ try
+ {
+ if (isConnected())
+ disconnect();
+ else if (m_socket)
+ internalDisconnect();
+ }
+ catch (vmime::exception&)
+ {
+ // Ignore
+ }
}
@@ -87,15 +99,16 @@ void SMTPTransport::connect()
m_socket = sf->create();
m_socket->connect(address, port);
+ m_responseBuffer.clear();
+
// Connection
//
// eg: C: <connection to server>
// --- S: 220 smtp.domain.com Service ready
string response;
- readResponse(response);
- if (responseCode(response) != 220)
+ if (readAllResponses(response) != 220)
{
internalDisconnect();
throw exceptions::connection_greeting_error(response);
@@ -105,12 +118,12 @@ void SMTPTransport::connect()
// First, try Extended SMTP (ESMTP)
//
// eg: C: EHLO thismachine.ourdomain.com
- // S: 250 OK
+ // S: 250-smtp.theserver.com
+ // S: 250 AUTH CRAM-MD5 DIGEST-MD5
sendRequest("EHLO " + platformDependant::getHandler()->getHostName());
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response, true) != 250)
{
// Next, try "Basic" SMTP
//
@@ -118,9 +131,8 @@ void SMTPTransport::connect()
// S: 250 OK
sendRequest("HELO " + platformDependant::getHandler()->getHostName());
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::connection_greeting_error(response);
@@ -131,93 +143,231 @@ void SMTPTransport::connect()
else
{
m_extendedSMTP = true;
+ m_extendedSMTPResponse = response;
}
// Authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH))
+ authenticate();
+}
+
+
+void SMTPTransport::authenticate()
+{
+ if (!m_extendedSMTP)
{
- if (!m_extendedSMTP)
+ internalDisconnect();
+ throw exceptions::command_error("AUTH", "ESMTP not supported.");
+ }
+
+ getAuthenticator()->setService(thisRef().dynamicCast <service>());
+
+#if VMIME_HAVE_SASL_SUPPORT
+ // First, try SASL authentication
+ if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
+ {
+ try
+ {
+ authenticateSASL();
+
+ m_authentified = true;
+ return;
+ }
+ catch (exceptions::authentication_error& e)
+ {
+ if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
+ {
+ // Can't fallback on normal authentication
+ internalDisconnect();
+ throw e;
+ }
+ else
+ {
+ // Ignore, will try normal authentication
+ }
+ }
+ catch (exception& e)
{
internalDisconnect();
- throw exceptions::command_error("AUTH", "ESMTP not supported.");
+ throw e;
}
+ }
+#endif // VMIME_HAVE_SASL_SUPPORT
- const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
- bool authentified = false;
+ // No other authentication method is possible
+ throw exceptions::authentication_error("All authentication methods failed");
+}
- enum AuthMethods
- {
- First = 0,
- CRAM_MD5 = First,
- // TODO: more authentication methods...
- End
- };
- for (int currentMethod = First ; !authentified ; ++currentMethod)
+#if VMIME_HAVE_SASL_SUPPORT
+
+void SMTPTransport::authenticateSASL()
+{
+ if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
+ throw exceptions::authentication_error("No SASL authenticator available.");
+
+ // Obtain SASL mechanisms supported by server from EHLO response
+ std::vector <string> saslMechs;
+ std::istringstream iss(m_extendedSMTPResponse);
+
+ while (!iss.eof())
+ {
+ string line;
+ std::getline(iss, line);
+
+ std::istringstream liss(line);
+ string word;
+
+ bool inAuth = false;
+
+ while (liss >> word)
{
- switch (currentMethod)
+ if (word.length() == 4 &&
+ (word[0] == 'A' || word[0] == 'a') ||
+ (word[0] == 'U' || word[0] == 'u') ||
+ (word[0] == 'T' || word[0] == 't') ||
+ (word[0] == 'H' || word[0] == 'h'))
{
- case CRAM_MD5:
+ inAuth = true;
+ }
+ else if (inAuth)
{
- sendRequest("AUTH CRAM-MD5");
- readResponse(response);
+ saslMechs.push_back(word);
+ }
+ }
+ }
- if (responseCode(response) == 334)
- {
- encoderB64 base64;
+ if (saslMechs.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
- string challengeB64 = responseText(response);
- string challenge, challengeHex;
+ std::vector <ref <security::sasl::SASLMechanism> > mechList;
- {
- utility::inputStreamStringAdapter in(challengeB64);
- utility::outputStreamStringAdapter out(challenge);
+ ref <security::sasl::SASLContext> saslContext =
+ vmime::create <security::sasl::SASLContext>();
- base64.decode(in, out);
- }
+ for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
+ {
+ try
+ {
+ mechList.push_back
+ (saslContext->createMechanism(saslMechs[i]));
+ }
+ catch (exceptions::no_such_mechanism&)
+ {
+ // Ignore mechanism
+ }
+ }
- hmac_md5(challenge, auth.getPassword(), challengeHex);
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
- string decoded = auth.getUsername() + " " + challengeHex;
- string encoded;
+ // Try to suggest a mechanism among all those supported
+ ref <security::sasl::SASLMechanism> suggestedMech =
+ saslContext->suggestMechanism(mechList);
- {
- utility::inputStreamStringAdapter in(decoded);
- utility::outputStreamStringAdapter out(encoded);
+ if (!suggestedMech)
+ throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
- base64.encode(in, out);
- }
+ // Allow application to choose which mechanisms to use
+ mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
+ getAcceptableMechanisms(mechList, suggestedMech);
- sendRequest(encoded);
- readResponse(response);
+ if (mechList.empty())
+ throw exceptions::authentication_error("No SASL mechanism available.");
- if (responseCode(response) == 235)
+ // Try each mechanism in the list in turn
+ for (unsigned int i = 0 ; i < mechList.size() ; ++i)
+ {
+ ref <security::sasl::SASLMechanism> mech = mechList[i];
+
+ ref <security::sasl::SASLSession> saslSession =
+ saslContext->createSession("smtp", getAuthenticator(), mech);
+
+ saslSession->init();
+
+ sendRequest("AUTH " + mech->getName());
+
+ for (bool cont = true ; cont ; )
+ {
+ string response;
+
+ switch (readAllResponses(response))
+ {
+ case 235:
+ {
+ m_socket = saslSession->getSecuredSocket(m_socket);
+ return;
+ }
+ case 334:
+ {
+ byte* challenge = 0;
+ int challengeLen = 0;
+
+ byte* resp = 0;
+ int respLen = 0;
+
+ try
+ {
+ // Extract challenge
+ saslContext->decodeB64(response, &challenge, &challengeLen);
+
+ // Prepare response
+ saslSession->evaluateChallenge
+ (challenge, challengeLen, &resp, &respLen);
+
+ // Send response
+ sendRequest(saslContext->encodeB64(resp, respLen));
+ }
+ catch (exceptions::sasl_exception& e)
+ {
+ if (challenge)
{
- authentified = true;
+ delete [] challenge;
+ challenge = NULL;
}
- else
+
+ if (resp)
{
- internalDisconnect();
- throw exceptions::authentication_error(response);
+ delete [] resp;
+ resp = NULL;
}
+
+ // Cancel SASL exchange
+ sendRequest("*");
+ }
+ catch (...)
+ {
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+
+ throw;
}
+ if (challenge)
+ delete [] challenge;
+
+ if (resp)
+ delete [] resp;
+
break;
}
- case End:
- {
- // All authentication methods have been tried and
- // the server does not understand any.
- throw exceptions::authentication_error(response);
- }
+ default:
+ cont = false;
+ break;
}
}
}
- m_authentified = true;
+ throw exceptions::authentication_error
+ ("Could not authenticate using SASL: all mechanisms failed.");
}
+#endif // VMIME_HAVE_SASL_SUPPORT
+
const bool SMTPTransport::isConnected() const
{
@@ -250,12 +400,11 @@ void SMTPTransport::internalDisconnect()
void SMTPTransport::noop()
{
- m_socket->send("NOOP");
+ sendRequest("NOOP");
string response;
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response) != 250)
throw exceptions::command_error("NOOP", response);
}
@@ -274,9 +423,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
string response;
sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">");
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("MAIL", response);
@@ -288,9 +436,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
const mailbox& mbox = *recipients.getMailboxAt(i);
sendRequest("RCPT TO: <" + mbox.getEmail() + ">");
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("RCPT TO", response);
@@ -299,9 +446,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
// Send the message data
sendRequest("DATA");
- readResponse(response);
- if (responseCode(response) != 354)
+ if (readAllResponses(response) != 354)
{
internalDisconnect();
throw exceptions::command_error("DATA", response);
@@ -315,9 +461,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
// Send end-of-data delimiter
m_socket->sendRaw("\r\n.\r\n", 5);
- readResponse(response);
- if (responseCode(response) != 250)
+ if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("DATA", response);
@@ -332,7 +477,7 @@ void SMTPTransport::sendRequest(const string& buffer, const bool end)
}
-const int SMTPTransport::responseCode(const string& response)
+const int SMTPTransport::getResponseCode(const string& response)
{
int code = 0;
@@ -347,35 +492,25 @@ const int SMTPTransport::responseCode(const string& response)
}
-const string SMTPTransport::responseText(const string& response)
+const string SMTPTransport::readResponseLine()
{
- string text;
+ string currentBuffer = m_responseBuffer;
- std::istringstream iss(response);
- std::string line;
-
- while (std::getline(iss, line))
+ while (true)
{
- if (line.length() >= 4)
- text += line.substr(4);
- else
- text += line;
-
- text += "\n";
- }
-
- return (text);
-}
+ // Get a line from the response buffer
+ string::size_type lineEnd = currentBuffer.find_first_of('\n');
+ if (lineEnd != string::npos)
+ {
+ const string line(currentBuffer.begin(), currentBuffer.begin() + lineEnd);
-void SMTPTransport::readResponse(string& buffer)
-{
- bool foundTerminator = false;
+ currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1);
+ m_responseBuffer = currentBuffer;
- buffer.clear();
+ return line;
+ }
- for ( ; !foundTerminator ; )
- {
// Check whether the time-out delay is elapsed
if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
{
@@ -393,40 +528,61 @@ void SMTPTransport::readResponse(string& buffer)
continue;
}
- // We have received data: reset the time-out counter
- if (m_timeoutHandler)
- m_timeoutHandler->resetTimeOut();
+ currentBuffer += receiveBuffer;
+ }
+}
- // 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;
+const int SMTPTransport::readResponse(string& text)
+{
+ string line = readResponseLine();
- for ( ; !end ; --p)
- {
- if (p == 0 || buffer[p] == '\n')
- {
- end = true;
+ // Special case where CRLF occurs after response code
+ if (line.length() < 4)
+ line = line + '\n' + readResponseLine();
- if (p + 4 < buffer.length())
- foundTerminator = true;
- }
- }
- }
- }
+ const int code = getResponseCode(line);
+
+ m_responseContinues = (line.length() >= 4 && line[3] == '-');
+
+ if (line.length() > 4)
+ text = utility::stringUtils::trim(line.substr(4));
+ else
+ text = utility::stringUtils::trim(line);
- // Remove [CR]LF at the end of the response
- if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n')
+ return code;
+}
+
+
+const int SMTPTransport::readAllResponses(string& outText, const bool allText)
+{
+ string text;
+
+ const int firstCode = readResponse(outText);
+
+ if (allText)
+ text = outText;
+
+ while (m_responseContinues)
{
- if (buffer[buffer.length() - 2] == '\r')
- buffer.resize(buffer.length() - 2);
- else
- buffer.resize(buffer.length() - 1);
+ const int code = readResponse(outText);
+
+ if (allText)
+ text += '\n' + outText;
+
+ if (code != firstCode)
+ {
+ if (allText)
+ outText = text;
+
+ return 0;
+ }
}
+
+ if (allText)
+ outText = text;
+
+ return firstCode;
}
@@ -460,6 +616,10 @@ const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const
{
// SMTP-specific options
property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"),
+#if VMIME_HAVE_SASL_SUPPORT
+ property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
+ property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "false"),
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME),
@@ -483,6 +643,10 @@ const std::vector <serviceInfos::property> SMTPTransport::_infos::getAvailablePr
// SMTP-specific options
list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH);
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);
diff --git a/src/net/transport.cpp b/src/net/transport.cpp
index 4f9acbeb..be0837cb 100644
--- a/src/net/transport.cpp
+++ b/src/net/transport.cpp
@@ -28,7 +28,7 @@ namespace vmime {
namespace net {
-transport::transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+transport::transport(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth)
: service(sess, infos, auth)
{
}
diff --git a/src/security/defaultAuthenticator.cpp b/src/security/defaultAuthenticator.cpp
new file mode 100644
index 00000000..913eb07a
--- /dev/null
+++ b/src/security/defaultAuthenticator.cpp
@@ -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.
+//
+
+#include "vmime/security/defaultAuthenticator.hpp"
+
+#include "vmime/net/service.hpp"
+
+#include "vmime/platformDependant.hpp"
+
+
+namespace vmime {
+namespace security {
+
+
+defaultAuthenticator::defaultAuthenticator()
+{
+}
+
+
+defaultAuthenticator::~defaultAuthenticator()
+{
+}
+
+
+const string defaultAuthenticator::getUsername() const
+{
+ const string& prefix = m_service->getInfos().getPropertyPrefix();
+ const propertySet& props = m_service->getSession()->getProperties();
+
+ if (props.hasProperty(prefix + net::serviceInfos::property::AUTH_USERNAME.getName()))
+ return props[prefix + net::serviceInfos::property::AUTH_USERNAME.getName()];
+
+ throw exceptions::no_auth_information();
+}
+
+
+const string defaultAuthenticator::getPassword() const
+{
+ const string& prefix = m_service->getInfos().getPropertyPrefix();
+ const propertySet& props = m_service->getSession()->getProperties();
+
+ if (props.hasProperty(prefix + net::serviceInfos::property::AUTH_PASSWORD.getName()))
+ return props[prefix + net::serviceInfos::property::AUTH_PASSWORD.getName()];
+
+ throw exceptions::no_auth_information();
+}
+
+
+const string defaultAuthenticator::getHostname() const
+{
+ return platformDependant::getHandler()->getHostName();
+}
+
+
+const string defaultAuthenticator::getAnonymousToken() const
+{
+ return "anonymous@" + platformDependant::getHandler()->getHostName();
+}
+
+
+const string defaultAuthenticator::getServiceName() const
+{
+ // Information cannot be provided
+ throw exceptions::no_auth_information();
+}
+
+
+void defaultAuthenticator::setService(ref <net::service> serv)
+{
+ m_service = serv;
+}
+
+
+weak_ref <net::service> defaultAuthenticator::getService() const
+{
+ return m_service;
+}
+
+
+} // security
+} // vmime
+
diff --git a/src/security/sasl/SASLContext.cpp b/src/security/sasl/SASLContext.cpp
new file mode 100644
index 00000000..dd8cbd93
--- /dev/null
+++ b/src/security/sasl/SASLContext.cpp
@@ -0,0 +1,189 @@
+//
+// 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 <sstream>
+
+#include <gsasl.h>
+
+#include "vmime/security/sasl/SASLContext.hpp"
+#include "vmime/security/sasl/SASLMechanism.hpp"
+
+#include "vmime/base.hpp"
+#include "vmime/encoderFactory.hpp"
+
+#include "vmime/utility/stream.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+SASLContext::SASLContext()
+{
+ if (gsasl_init(&m_gsaslContext) != GSASL_OK)
+ throw std::bad_alloc();
+}
+
+
+SASLContext::~SASLContext()
+{
+ gsasl_done(m_gsaslContext);
+}
+
+
+ref <SASLSession> SASLContext::createSession
+ (const string& serviceName,
+ ref <authenticator> auth, ref <SASLMechanism> mech)
+{
+ return vmime::create <SASLSession>
+ (serviceName, thisRef().dynamicCast <SASLContext>(), auth, mech);
+}
+
+
+ref <SASLMechanism> SASLContext::createMechanism(const string& name)
+{
+ return SASLMechanismFactory::getInstance()->create
+ (thisRef().dynamicCast <SASLContext>(), name);
+}
+
+
+ref <SASLMechanism> SASLContext::suggestMechanism
+ (const std::vector <ref <SASLMechanism> >& mechs)
+{
+ if (mechs.empty())
+ return 0;
+
+ std::ostringstream oss;
+
+ for (unsigned int i = 0 ; i < mechs.size() ; ++i)
+ oss << mechs[i]->getName() << " ";
+
+ const string mechList = oss.str();
+ const char* suggested = gsasl_client_suggest_mechanism
+ (m_gsaslContext, mechList.c_str());
+
+ if (suggested)
+ {
+ for (unsigned int i = 0 ; i < mechs.size() ; ++i)
+ {
+ if (mechs[i]->getName() == suggested)
+ return mechs[i];
+ }
+ }
+
+ return 0;
+}
+
+
+void SASLContext::decodeB64(const string& input, byte** output, int* outputLen)
+{
+ string res;
+
+ utility::inputStreamStringAdapter is(input);
+ utility::outputStreamStringAdapter os(res);
+
+ ref <encoder> dec = encoderFactory::getInstance()->create("base64");
+
+ dec->decode(is, os);
+
+ byte* out = new byte[res.length()];
+
+ std::copy(res.begin(), res.end(), out);
+
+ *output = out;
+ *outputLen = res.length();
+}
+
+
+const string SASLContext::encodeB64(const byte* input, const int inputLen)
+{
+ string res;
+
+ utility::inputStreamByteBufferAdapter is(input, inputLen);
+ utility::outputStreamStringAdapter os(res);
+
+ ref <encoder> enc = encoderFactory::getInstance()->create("base64");
+
+ enc->encode(is, os);
+
+ return res;
+}
+
+
+const string SASLContext::getErrorMessage(const string& fname, const int code)
+{
+ string msg = fname + "() returned ";
+
+#define ERROR(x) \
+ case x: msg += #x; break;
+
+ switch (code)
+ {
+ ERROR(GSASL_NEEDS_MORE)
+ ERROR(GSASL_UNKNOWN_MECHANISM)
+ ERROR(GSASL_MECHANISM_CALLED_TOO_MANY_TIMES)
+ ERROR(GSASL_MALLOC_ERROR)
+ ERROR(GSASL_BASE64_ERROR)
+ ERROR(GSASL_CRYPTO_ERROR)
+ ERROR(GSASL_SASLPREP_ERROR)
+ ERROR(GSASL_MECHANISM_PARSE_ERROR)
+ ERROR(GSASL_AUTHENTICATION_ERROR)
+ ERROR(GSASL_INTEGRITY_ERROR)
+ ERROR(GSASL_NO_CLIENT_CODE)
+ ERROR(GSASL_NO_SERVER_CODE)
+ ERROR(GSASL_NO_CALLBACK)
+ ERROR(GSASL_NO_ANONYMOUS_TOKEN)
+ ERROR(GSASL_NO_AUTHID)
+ ERROR(GSASL_NO_AUTHZID)
+ ERROR(GSASL_NO_PASSWORD)
+ ERROR(GSASL_NO_PASSCODE)
+ ERROR(GSASL_NO_PIN)
+ ERROR(GSASL_NO_SERVICE)
+ ERROR(GSASL_NO_HOSTNAME)
+ ERROR(GSASL_GSSAPI_RELEASE_BUFFER_ERROR)
+ ERROR(GSASL_GSSAPI_IMPORT_NAME_ERROR)
+ ERROR(GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR)
+ ERROR(GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR)
+ ERROR(GSASL_GSSAPI_UNWRAP_ERROR)
+ ERROR(GSASL_GSSAPI_WRAP_ERROR)
+ ERROR(GSASL_GSSAPI_ACQUIRE_CRED_ERROR)
+ ERROR(GSASL_GSSAPI_DISPLAY_NAME_ERROR)
+ ERROR(GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR)
+ ERROR(GSASL_KERBEROS_V5_INIT_ERROR)
+ ERROR(GSASL_KERBEROS_V5_INTERNAL_ERROR)
+ ERROR(GSASL_SECURID_SERVER_NEED_ADDITIONAL_PASSCODE)
+ ERROR(GSASL_SECURID_SERVER_NEED_NEW_PIN)
+
+ default:
+
+ msg += "unknown error";
+ break;
+ }
+
+#undef ERROR
+
+ return msg;
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/security/sasl/SASLMechanismFactory.cpp b/src/security/sasl/SASLMechanismFactory.cpp
new file mode 100644
index 00000000..fda4448c
--- /dev/null
+++ b/src/security/sasl/SASLMechanismFactory.cpp
@@ -0,0 +1,131 @@
+//
+// 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 <stdexcept>
+#include <new>
+
+#include <gsasl.h>
+
+#include "vmime/security/sasl/SASLMechanismFactory.hpp"
+#include "vmime/security/sasl/builtinSASLMechanism.hpp"
+#include "vmime/security/sasl/SASLContext.hpp"
+
+#include "vmime/utility/stringUtils.hpp"
+
+#include "vmime/base.hpp"
+#include "vmime/exception.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+SASLMechanismFactory::SASLMechanismFactory()
+{
+ if (gsasl_init(&m_gsaslContext) != GSASL_OK)
+ throw std::bad_alloc();
+}
+
+
+SASLMechanismFactory::~SASLMechanismFactory()
+{
+ gsasl_done(m_gsaslContext);
+}
+
+
+// static
+SASLMechanismFactory* SASLMechanismFactory::getInstance()
+{
+ static SASLMechanismFactory instance;
+ return &instance;
+}
+
+
+ref <SASLMechanism> SASLMechanismFactory::create
+ (ref <SASLContext> ctx, const string& name_)
+{
+ const string name(utility::stringUtils::toUpper(name_));
+
+ // Check for built-in mechanisms
+ if (isMechanismSupported(name))
+ {
+ return vmime::create <builtinSASLMechanism>(ctx, name);
+ }
+ // Check for registered mechanisms
+ else
+ {
+ MapType::iterator it = m_mechs.find(name);
+
+ if (it != m_mechs.end())
+ return (*it).second->create(ctx, name);
+ }
+
+ throw exceptions::no_such_mechanism(name);
+ return 0;
+}
+
+
+const std::vector <string> SASLMechanismFactory::getSupportedMechanisms() const
+{
+ std::vector <string> list;
+
+ // Registered mechanisms
+ for (MapType::const_iterator it = m_mechs.begin() ;
+ it != m_mechs.end() ; ++it)
+ {
+ list.push_back((*it).first);
+ }
+
+ // Built-in mechanisms
+ char* out = 0;
+
+ if (gsasl_client_mechlist(m_gsaslContext, &out) == GSASL_OK)
+ {
+ // 'out' contains SASL mechanism names, separated by spaces
+ for (char *start = out, *p = out ; ; ++p)
+ {
+ if (*p == ' ' || !*p)
+ {
+ list.push_back(string(start, p));
+ start = p + 1;
+
+ // End of string
+ if (!*p) break;
+ }
+ }
+
+ free(out);
+ }
+
+ return list;
+}
+
+
+const bool SASLMechanismFactory::isMechanismSupported(const string& name) const
+{
+ return (gsasl_client_support_p(m_gsaslContext, name.c_str()) != 0 ||
+ m_mechs.find(name) != m_mechs.end());
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/security/sasl/SASLSession.cpp b/src/security/sasl/SASLSession.cpp
new file mode 100644
index 00000000..f4d8bf13
--- /dev/null
+++ b/src/security/sasl/SASLSession.cpp
@@ -0,0 +1,179 @@
+//
+// 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 <sstream>
+
+#include <gsasl.h>
+
+#include "vmime/security/sasl/SASLSession.hpp"
+
+#include "vmime/security/sasl/SASLContext.hpp"
+#include "vmime/security/sasl/SASLSocket.hpp"
+#include "vmime/security/sasl/SASLAuthenticator.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+SASLSession::SASLSession(const string& serviceName, ref <SASLContext> ctx,
+ ref <authenticator> auth, ref <SASLMechanism> mech)
+ : m_serviceName(serviceName), m_context(ctx), m_auth(auth),
+ m_mech(mech), m_gsaslContext(0), m_gsaslSession(0)
+{
+ if (gsasl_init(&m_gsaslContext) != GSASL_OK)
+ throw std::bad_alloc();
+
+ gsasl_client_start(m_gsaslContext, mech->getName().c_str(), &m_gsaslSession);
+
+ gsasl_callback_set(m_gsaslContext, gsaslCallback);
+ gsasl_callback_hook_set(m_gsaslContext, this);
+}
+
+
+SASLSession::~SASLSession()
+{
+ gsasl_finish(m_gsaslSession);
+ m_gsaslSession = 0;
+
+ gsasl_done(m_gsaslContext);
+ m_gsaslContext = 0;
+}
+
+
+void SASLSession::init()
+{
+ ref <SASLAuthenticator> saslAuth = m_auth.dynamicCast <SASLAuthenticator>();
+
+ if (saslAuth)
+ {
+ saslAuth->setSASLMechanism(m_mech);
+ saslAuth->setSASLSession(thisRef().dynamicCast <SASLSession>());
+ }
+}
+
+
+ref <authenticator> SASLSession::getAuthenticator()
+{
+ return m_auth;
+}
+
+
+ref <SASLMechanism> SASLSession::getMechanism()
+{
+ return m_mech;
+}
+
+
+ref <SASLContext> SASLSession::getContext()
+{
+ return m_context;
+}
+
+
+const bool SASLSession::evaluateChallenge
+ (const byte* challenge, const int challengeLen,
+ byte** response, int* responseLen)
+{
+ return m_mech->step(thisRef().dynamicCast <SASLSession>(),
+ challenge, challengeLen, response, responseLen);
+}
+
+
+ref <net::socket> SASLSession::getSecuredSocket(ref <net::socket> sok)
+{
+ return vmime::create <SASLSocket>(thisRef().dynamicCast <SASLSession>(), sok);
+}
+
+
+const string SASLSession::getServiceName() const
+{
+ return m_serviceName;
+}
+
+
+// static
+int SASLSession::gsaslCallback
+ (Gsasl* ctx, Gsasl_session* sctx, Gsasl_property prop)
+{
+ SASLSession* sess = reinterpret_cast <SASLSession*>(gsasl_callback_hook_get(ctx));
+ if (!sess) return GSASL_AUTHENTICATION_ERROR;
+
+ ref <authenticator> auth = sess->getAuthenticator();
+
+ try
+ {
+ string res;
+
+ switch (prop)
+ {
+ case GSASL_AUTHID:
+
+ res = auth->getUsername();
+ break;
+
+ case GSASL_PASSWORD:
+
+ res = auth->getPassword();
+ break;
+
+ case GSASL_ANONYMOUS_TOKEN:
+
+ res = auth->getAnonymousToken();
+ break;
+
+ case GSASL_HOSTNAME:
+
+ res = auth->getHostname();
+ break;
+
+ case GSASL_SERVICE:
+
+ res = auth->getServiceName();
+ break;
+
+ case GSASL_AUTHZID:
+ case GSASL_GSSAPI_DISPLAY_NAME:
+ case GSASL_PASSCODE:
+ case GSASL_SUGGESTED_PIN:
+ case GSASL_PIN:
+ case GSASL_REALM:
+
+ default:
+
+ return GSASL_NO_CALLBACK;
+ }
+
+ gsasl_property_set(sctx, prop, res.c_str());
+
+ return GSASL_OK;
+ }
+ //catch (exceptions::no_auth_information&)
+ catch (...)
+ {
+ return GSASL_NO_CALLBACK;
+ }
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/security/sasl/SASLSocket.cpp b/src/security/sasl/SASLSocket.cpp
new file mode 100644
index 00000000..321f90f7
--- /dev/null
+++ b/src/security/sasl/SASLSocket.cpp
@@ -0,0 +1,167 @@
+//
+// 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/security/sasl/SASLSocket.hpp"
+#include "vmime/security/sasl/SASLSession.hpp"
+
+#include "vmime/exception.hpp"
+
+#include <algorithm>
+
+#include <gsasl.h>
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+
+SASLSocket::SASLSocket(ref <SASLSession> sess, ref <net::socket> wrapped)
+ : m_session(sess), m_wrapped(wrapped),
+ m_pendingBuffer(0), m_pendingPos(0), m_pendingLen(0)
+{
+}
+
+
+SASLSocket::~SASLSocket()
+{
+ if (m_pendingBuffer)
+ delete [] m_pendingBuffer;
+}
+
+
+void SASLSocket::connect(const string& address, const port_t port)
+{
+ m_wrapped->connect(address, port);
+}
+
+
+void SASLSocket::disconnect()
+{
+ m_wrapped->disconnect();
+}
+
+
+const bool SASLSocket::isConnected() const
+{
+ return m_wrapped->isConnected();
+}
+
+
+void SASLSocket::receive(string& buffer)
+{
+ const int n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer));
+
+ buffer = string(m_recvBuffer, n);
+}
+
+
+const int SASLSocket::receiveRaw(char* buffer, const int count)
+{
+ if (m_pendingLen != 0)
+ {
+ const int copyLen =
+ (count >= m_pendingLen ? m_pendingLen : count);
+
+ std::copy(m_pendingBuffer + m_pendingPos,
+ m_pendingBuffer + m_pendingPos + copyLen,
+ buffer);
+
+ m_pendingLen -= copyLen;
+ m_pendingPos += copyLen;
+
+ if (m_pendingLen == 0)
+ {
+ delete [] m_pendingBuffer;
+
+ m_pendingBuffer = 0;
+ m_pendingPos = 0;
+ m_pendingLen = 0;
+ }
+
+ return copyLen;
+ }
+
+ const int n = m_wrapped->receiveRaw(buffer, count);
+
+ byte* output = 0;
+ int outputLen = 0;
+
+ m_session->getMechanism()->decode
+ (m_session, reinterpret_cast <const byte*>(buffer), n,
+ &output, &outputLen);
+
+ // If we can not copy all decoded data into the output buffer, put
+ // remaining data into a pending buffer for next calls to receive()
+ if (outputLen > count)
+ {
+ std::copy(output, output + count, buffer);
+
+ m_pendingBuffer = output;
+ m_pendingLen = outputLen;
+ m_pendingPos = count;
+
+ return count;
+ }
+ else
+ {
+ std::copy(output, output + outputLen, buffer);
+
+ delete [] output;
+
+ return outputLen;
+ }
+}
+
+
+void SASLSocket::send(const string& buffer)
+{
+ sendRaw(buffer.data(), buffer.length());
+}
+
+
+void SASLSocket::sendRaw(const char* buffer, const int count)
+{
+ byte* output = 0;
+ int outputLen = 0;
+
+ m_session->getMechanism()->encode
+ (m_session, reinterpret_cast <const byte*>(buffer), count,
+ &output, &outputLen);
+
+ try
+ {
+ m_wrapped->sendRaw
+ (reinterpret_cast <const char*>(output), outputLen);
+ }
+ catch (...)
+ {
+ delete [] output;
+ throw;
+ }
+
+ delete [] output;
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/security/sasl/builtinSASLMechanism.cpp b/src/security/sasl/builtinSASLMechanism.cpp
new file mode 100644
index 00000000..89704eeb
--- /dev/null
+++ b/src/security/sasl/builtinSASLMechanism.cpp
@@ -0,0 +1,182 @@
+//
+// 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 <gsasl.h>
+
+#include "vmime/security/sasl/builtinSASLMechanism.hpp"
+
+#include "vmime/security/sasl/SASLContext.hpp"
+#include "vmime/security/sasl/SASLSession.hpp"
+
+#include "vmime/exception.hpp"
+
+#include <stdexcept>
+#include <new>
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+builtinSASLMechanism::builtinSASLMechanism(ref <SASLContext> ctx, const string& name)
+ : m_context(ctx), m_name(name), m_complete(false)
+{
+}
+
+
+builtinSASLMechanism::~builtinSASLMechanism()
+{
+}
+
+
+const string builtinSASLMechanism::getName() const
+{
+ return m_name;
+}
+
+
+const bool builtinSASLMechanism::step
+ (ref <SASLSession> sess, const byte* challenge, const int challengeLen,
+ byte** response, int* responseLen)
+{
+ char* output = 0;
+ size_t outputLen = 0;
+
+ const int result = gsasl_step(sess->m_gsaslSession,
+ reinterpret_cast <const char*>(challenge), challengeLen,
+ &output, &outputLen);
+
+ if (result == GSASL_OK || result == GSASL_NEEDS_MORE)
+ {
+ byte* res = new byte[outputLen];
+
+ for (size_t i = 0 ; i < outputLen ; ++i)
+ res[i] = output[i];
+
+ *response = res;
+ *responseLen = outputLen;
+
+ free(output);
+ }
+ else
+ {
+ *response = 0;
+ *responseLen = 0;
+ }
+
+ if (result == GSASL_OK)
+ {
+ // Authentication process completed
+ m_complete = true;
+ return true;
+ }
+ else if (result == GSASL_NEEDS_MORE)
+ {
+ // Continue authentication process
+ return false;
+ }
+ else if (result == GSASL_MALLOC_ERROR)
+ {
+ throw std::bad_alloc();
+ }
+ else
+ {
+ throw exceptions::sasl_exception("Error when processing challenge: "
+ + SASLContext::getErrorMessage("gsasl_step", result));
+ }
+}
+
+
+const bool builtinSASLMechanism::isComplete() const
+{
+ return m_complete;
+}
+
+
+void builtinSASLMechanism::encode
+ (ref <SASLSession> sess, const byte* input, const int inputLen,
+ byte** output, int* outputLen)
+{
+ char* coutput = 0;
+ size_t coutputLen = 0;
+
+ if (gsasl_encode(sess->m_gsaslSession,
+ reinterpret_cast <const char*>(input), inputLen,
+ &coutput, &coutputLen) != GSASL_OK)
+ {
+ throw exceptions::sasl_exception("Encoding error.");
+ }
+
+ try
+ {
+ byte* res = new byte[coutputLen];
+
+ std::copy(coutput, coutput + coutputLen, res);
+
+ *output = res;
+ *outputLen = static_cast <int>(coutputLen);
+ }
+ catch (...)
+ {
+ free(coutput);
+ throw;
+ }
+
+ free(coutput);
+}
+
+
+void builtinSASLMechanism::decode
+ (ref <SASLSession> sess, const byte* input, const int inputLen,
+ byte** output, int* outputLen)
+{
+ char* coutput = 0;
+ size_t coutputLen = 0;
+
+ try
+ {
+ if (gsasl_decode(sess->m_gsaslSession,
+ reinterpret_cast <const char*>(input), inputLen,
+ &coutput, &coutputLen) != GSASL_OK)
+ {
+ throw exceptions::sasl_exception("Decoding error.");
+ }
+
+ byte* res = new byte[coutputLen];
+
+ std::copy(coutput, coutput + coutputLen, res);
+
+ *output = res;
+ *outputLen = static_cast <int>(coutputLen);
+ }
+ catch (...)
+ {
+ free(coutput);
+ throw;
+ }
+
+ free(coutput);
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/security/sasl/defaultSASLAuthenticator.cpp b/src/security/sasl/defaultSASLAuthenticator.cpp
new file mode 100644
index 00000000..d396e069
--- /dev/null
+++ b/src/security/sasl/defaultSASLAuthenticator.cpp
@@ -0,0 +1,139 @@
+//
+// 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/security/sasl/defaultSASLAuthenticator.hpp"
+
+#include "vmime/security/sasl/SASLMechanism.hpp"
+#include "vmime/security/sasl/SASLSession.hpp"
+
+#include "vmime/net/service.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+defaultSASLAuthenticator::defaultSASLAuthenticator()
+{
+}
+
+
+defaultSASLAuthenticator::~defaultSASLAuthenticator()
+{
+}
+
+
+const std::vector <ref <SASLMechanism> >
+ defaultSASLAuthenticator::getAcceptableMechanisms
+ (const std::vector <ref <SASLMechanism> >& available,
+ ref <SASLMechanism> suggested) const
+{
+ if (suggested)
+ {
+ std::vector <ref <SASLMechanism> > res;
+
+ res.push_back(suggested);
+
+ for (unsigned int i = 0 ; i < available.size() ; ++i)
+ {
+ if (available[i]->getName() != suggested->getName())
+ res.push_back(available[i]);
+ }
+
+ return res;
+ }
+ else
+ {
+ return available;
+ }
+}
+
+
+const string defaultSASLAuthenticator::getUsername() const
+{
+ return m_default.getUsername();
+}
+
+
+const string defaultSASLAuthenticator::getPassword() const
+{
+ return m_default.getPassword();
+}
+
+
+const string defaultSASLAuthenticator::getHostname() const
+{
+ return m_default.getHostname();
+}
+
+
+const string defaultSASLAuthenticator::getAnonymousToken() const
+{
+ return m_default.getAnonymousToken();
+}
+
+
+const string defaultSASLAuthenticator::getServiceName() const
+{
+ return m_saslSession->getServiceName();
+}
+
+
+void defaultSASLAuthenticator::setService(ref <net::service> serv)
+{
+ m_service = serv;
+ m_default.setService(serv);
+}
+
+
+weak_ref <net::service> defaultSASLAuthenticator::getService() const
+{
+ return m_service;
+}
+
+
+void defaultSASLAuthenticator::setSASLSession(ref <SASLSession> sess)
+{
+ m_saslSession = sess;
+}
+
+
+ref <SASLSession> defaultSASLAuthenticator::getSASLSession() const
+{
+ return m_saslSession;
+}
+
+
+void defaultSASLAuthenticator::setSASLMechanism(ref <SASLMechanism> mech)
+{
+ m_saslMech = mech;
+}
+
+
+ref <SASLMechanism> defaultSASLAuthenticator::getSASLMechanism() const
+{
+ return m_saslMech;
+}
+
+
+} // sasl
+} // security
+} // vmime
+
diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp
index 49ebcf66..c13e8df7 100644
--- a/src/utility/stream.cpp
+++ b/src/utility/stream.cpp
@@ -326,6 +326,67 @@ const stream::size_type inputStreamPointerAdapter::skip(const size_type count)
}
+
+// inputStreamByteBufferAdapter
+
+inputStreamByteBufferAdapter::inputStreamByteBufferAdapter(const byte* buffer, const size_type length)
+ : m_buffer(buffer), m_length(length), m_pos(0)
+{
+}
+
+
+const bool inputStreamByteBufferAdapter::eof() const
+{
+ return m_pos >= m_length;
+}
+
+
+void inputStreamByteBufferAdapter::reset()
+{
+ m_pos = 0;
+}
+
+
+const stream::size_type inputStreamByteBufferAdapter::read
+ (value_type* const data, const size_type count)
+{
+ const size_type remaining = m_length - m_pos;
+
+ if (remaining < count)
+ {
+ std::copy(m_buffer + m_pos, m_buffer + m_pos + remaining, data);
+ m_pos += remaining;
+
+ return remaining;
+ }
+ else
+ {
+ std::copy(m_buffer + m_pos, m_buffer + m_pos + count, data);
+ m_pos += count;
+
+ return count;
+ }
+}
+
+
+const stream::size_type inputStreamByteBufferAdapter::skip(const size_type count)
+{
+ const size_type remaining = m_length - m_pos;
+
+ if (remaining < count)
+ {
+ m_pos += remaining;
+ return remaining;
+ }
+ else
+ {
+ m_pos += count;
+ return count;
+ }
+}
+
+
+
#ifdef VMIME_HAVE_MESSAGING_FEATURES
diff --git a/vmime/exception.hpp b/vmime/exception.hpp
index d7f6912d..d7dc5564 100644
--- a/vmime/exception.hpp
+++ b/vmime/exception.hpp
@@ -811,6 +811,58 @@ public:
#endif // VMIME_HAVE_FILESYSTEM_FEATURES
+#if VMIME_HAVE_SASL_SUPPORT
+
+
+/** Base class for exceptions throw by SASL module.
+ */
+
+class sasl_exception : public vmime::exception
+{
+public:
+
+ sasl_exception(const string& what, const exception& other = NO_EXCEPTION);
+ ~sasl_exception() throw();
+
+ exception* clone() const;
+ const char* name() const throw();
+};
+
+
+/** No mechanism is registered with the specified name.
+ */
+
+class no_such_mechanism : public sasl_exception
+{
+public:
+
+ no_such_mechanism(const string& name, const exception& other = NO_EXCEPTION);
+ ~no_such_mechanism() throw();
+
+ exception* clone() const;
+ const char* name() const throw();
+};
+
+
+/** The requested information cannot be provided.
+ */
+
+class no_auth_information : public sasl_exception
+{
+public:
+
+ no_auth_information(const exception& other = NO_EXCEPTION);
+ ~no_auth_information() throw();
+
+ exception* clone() const;
+ const char* name() const throw();
+};
+
+
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
+
} // exceptions
diff --git a/vmime/net/authenticator.hpp b/vmime/net/authenticator.hpp
deleted file mode 100644
index 7e58a70b..00000000
--- a/vmime/net/authenticator.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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/imap/IMAPConnection.hpp b/vmime/net/imap/IMAPConnection.hpp
index 542f4869..87eb80e0 100644
--- a/vmime/net/imap/IMAPConnection.hpp
+++ b/vmime/net/imap/IMAPConnection.hpp
@@ -23,13 +23,14 @@
#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"
+#include "vmime/security/authenticator.hpp"
+
namespace vmime {
namespace net {
@@ -44,7 +45,7 @@ class IMAPConnection : public object
{
public:
- IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth);
+ IMAPConnection(weak_ref <IMAPStore> store, ref <security::authenticator> auth);
~IMAPConnection();
@@ -83,11 +84,21 @@ public:
ref <session> getSession();
+ const std::vector <string> getCapabilities();
+
+ ref <security::authenticator> getAuthenticator();
+
private:
+ void authenticate();
+#if VMIME_HAVE_SASL_SUPPORT
+ void authenticateSASL();
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
weak_ref <IMAPStore> m_store;
- ref <authenticator> m_auth;
+ ref <security::authenticator> m_auth;
ref <socket> m_socket;
diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp
index 090e6ffe..14d10b57 100644
--- a/vmime/net/imap/IMAPParser.hpp
+++ b/vmime/net/imap/IMAPParser.hpp
@@ -1401,22 +1401,32 @@ public:
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 (parser.check <one_char <'\\'> >(line, &pos, true))
+ {
+ atom* at = parser.get <atom>(line, &pos);
+ const string name = utility::stringUtils::toLower(at->value());
+ delete (at);
- if (name == "marked")
- m_type = MARKED;
- else if (name == "noinferiors")
- m_type = NOINFERIORS;
- else if (name == "noselect")
- m_type = NOSELECT;
- else if (name == "unmarked")
- m_type = UNMARKED;
+ if (name == "marked")
+ m_type = MARKED;
+ else if (name == "noinferiors")
+ m_type = NOINFERIORS;
+ else if (name == "noselect")
+ m_type = NOSELECT;
+ else if (name == "unmarked")
+ m_type = UNMARKED;
+ else
+ {
+ m_type = UNKNOWN;
+ m_name = "\\" + name;
+ }
+ }
else
{
+ atom* at = parser.get <atom>(line, &pos);
+ const string name = utility::stringUtils::toLower(at->value());
+ delete (at);
+
m_type = UNKNOWN;
m_name = name;
}
@@ -1989,40 +1999,13 @@ public:
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 ; )
+ while (parser.check <SPACE>(line, &pos, true))
{
- 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);
+ capability* cap = parser.get <capability>(line, &pos);
+ if (cap == NULL) break;
- parser.check <SPACE>(line, &pos);
- }
+ m_capabilities.push_back(cap);
}
*currentPos = pos;
diff --git a/vmime/net/imap/IMAPStore.hpp b/vmime/net/imap/IMAPStore.hpp
index 887894f4..506ba42f 100644
--- a/vmime/net/imap/IMAPStore.hpp
+++ b/vmime/net/imap/IMAPStore.hpp
@@ -52,7 +52,7 @@ class IMAPStore : public store
public:
- IMAPStore(ref <session> sess, ref <authenticator> auth);
+ IMAPStore(ref <session> sess, ref <security::authenticator> auth);
~IMAPStore();
const string getProtocolName() const;
@@ -79,13 +79,6 @@ 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();
@@ -106,7 +99,10 @@ private:
struct props
{
// IMAP-specific options
- // (none)
+#if VMIME_HAVE_SASL_SUPPORT
+ serviceInfos::property PROPERTY_OPTIONS_SASL;
+ serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;
diff --git a/vmime/net/maildir/maildirStore.hpp b/vmime/net/maildir/maildirStore.hpp
index 32451808..bfaf0487 100644
--- a/vmime/net/maildir/maildirStore.hpp
+++ b/vmime/net/maildir/maildirStore.hpp
@@ -49,7 +49,7 @@ class maildirStore : public store
public:
- maildirStore(ref <session> sess, ref <authenticator> auth);
+ maildirStore(ref <session> sess, ref <security::authenticator> auth);
~maildirStore();
const string getProtocolName() const;
diff --git a/vmime/net/pop3/POP3Store.hpp b/vmime/net/pop3/POP3Store.hpp
index e50bbeef..e5ce7ee2 100644
--- a/vmime/net/pop3/POP3Store.hpp
+++ b/vmime/net/pop3/POP3Store.hpp
@@ -48,7 +48,7 @@ class POP3Store : public store
public:
- POP3Store(ref <session> sess, ref <authenticator> auth);
+ POP3Store(ref <session> sess, ref <security::authenticator> auth);
~POP3Store();
const string getProtocolName() const;
@@ -72,9 +72,24 @@ public:
private:
+ enum ResponseCode
+ {
+ RESPONSE_OK = 0,
+ RESPONSE_READY,
+ RESPONSE_ERR
+ };
+
+ void authenticate(const messageId& randomMID);
+#if VMIME_HAVE_SASL_SUPPORT
+ void authenticateSASL();
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ const std::vector <string> getCapabilities();
+
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);
+ static const int getResponseCode(const string& buffer);
void sendRequest(const string& buffer, const bool end = true);
void readResponse(string& buffer, const bool multiLine, utility::progressionListener* progress = NULL);
@@ -108,6 +123,10 @@ private:
// POP3-specific options
serviceInfos::property PROPERTY_OPTIONS_APOP;
serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK;
+#if VMIME_HAVE_SASL_SUPPORT
+ serviceInfos::property PROPERTY_OPTIONS_SASL;
+ serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;
diff --git a/vmime/net/sendmail/sendmailTransport.hpp b/vmime/net/sendmail/sendmailTransport.hpp
index 292f5ebe..94ec4c15 100644
--- a/vmime/net/sendmail/sendmailTransport.hpp
+++ b/vmime/net/sendmail/sendmailTransport.hpp
@@ -43,7 +43,7 @@ class sendmailTransport : public transport
{
public:
- sendmailTransport(ref <session> sess, ref <authenticator> auth);
+ sendmailTransport(ref <session> sess, ref <security::authenticator> auth);
~sendmailTransport();
const string getProtocolName() const;
diff --git a/vmime/net/service.hpp b/vmime/net/service.hpp
index cfb5c736..48dfdb71 100644
--- a/vmime/net/service.hpp
+++ b/vmime/net/service.hpp
@@ -24,7 +24,6 @@
#include "vmime/types.hpp"
#include "vmime/net/session.hpp"
-#include "vmime/net/authenticator.hpp"
#include "vmime/net/serviceFactory.hpp"
#include "vmime/net/serviceInfos.hpp"
@@ -43,7 +42,7 @@ class service : public object
{
protected:
- service(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth);
+ service(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth);
public:
@@ -110,13 +109,19 @@ public:
*
* @return authenticator object
*/
- ref <const authenticator> getAuthenticator() const;
+ ref <const security::authenticator> getAuthenticator() const;
/** Return the authenticator object used with this service instance.
*
* @return authenticator object
*/
- ref <authenticator> getAuthenticator();
+ ref <security::authenticator> getAuthenticator();
+
+ /** Set the authenticator object used with this service instance.
+ *
+ * @param auth authenticator object
+ */
+ void setAuthenticator(ref <security::authenticator> auth);
/** Set a property for this service (service prefix is added automatically).
*
@@ -150,7 +155,7 @@ public:
private:
ref <session> m_session;
- ref <authenticator> m_auth;
+ ref <security::authenticator> m_auth;
};
diff --git a/vmime/net/serviceFactory.hpp b/vmime/net/serviceFactory.hpp
index 2eb04e0e..209bdca5 100644
--- a/vmime/net/serviceFactory.hpp
+++ b/vmime/net/serviceFactory.hpp
@@ -30,9 +30,10 @@
#include "vmime/utility/url.hpp"
#include "vmime/net/serviceInfos.hpp"
-#include "vmime/net/authenticator.hpp"
#include "vmime/net/timeoutHandler.hpp"
+#include "vmime/security/authenticator.hpp"
+
#include "vmime/utility/progressionListener.hpp"
@@ -69,7 +70,9 @@ public:
public:
- virtual ref <service> create(ref <session> sess, ref <authenticator> auth) const = 0;
+ virtual ref <service> create
+ (ref <session> sess,
+ ref <security::authenticator> auth) const = 0;
virtual const string& getName() const = 0;
virtual const serviceInfos& getInfos() const = 0;
@@ -92,7 +95,9 @@ private:
public:
- ref <service> create(ref <session> sess, ref <authenticator> auth) const
+ ref <service> create
+ (ref <session> sess,
+ ref <security::authenticator> auth) const
{
return vmime::create <S>(sess, auth);
}
@@ -137,7 +142,10 @@ public:
* @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);
+ ref <service> create
+ (ref <session> sess,
+ const string& protocol,
+ ref <security::authenticator> auth = NULL);
/** Create a new service instance from a URL.
*
@@ -149,7 +157,10 @@ public:
* @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);
+ ref <service> create
+ (ref <session> sess,
+ const utility::url& u,
+ ref <security::authenticator> auth = NULL);
/** Return information about a registered protocol.
*
diff --git a/vmime/net/session.hpp b/vmime/net/session.hpp
index c01ae5f2..e0d960b6 100644
--- a/vmime/net/session.hpp
+++ b/vmime/net/session.hpp
@@ -21,7 +21,7 @@
#define VMIME_NET_SESSION_HPP_INCLUDED
-#include "vmime/net/authenticator.hpp"
+#include "vmime/security/authenticator.hpp"
#include "vmime/utility/url.hpp"
@@ -60,7 +60,8 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new transport service
*/
- ref <transport> getTransport(ref <authenticator> auth = NULL);
+ ref <transport> getTransport
+ (ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the specified protocol.
*
@@ -70,7 +71,9 @@ public:
* 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);
+ ref <transport> getTransport
+ (const string& protocol,
+ ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the specified URL.
*
@@ -80,7 +83,9 @@ public:
* 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);
+ ref <transport> getTransport
+ (const utility::url& url,
+ ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the protocol specified
* in the session properties.
@@ -92,7 +97,7 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new store service
*/
- ref <store> getStore(ref <authenticator> auth = NULL);
+ ref <store> getStore(ref <security::authenticator> auth = NULL);
/** Return a store service instance for the specified protocol.
*
@@ -102,7 +107,9 @@ public:
* 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);
+ ref <store> getStore
+ (const string& protocol,
+ ref <security::authenticator> auth = NULL);
/** Return a store service instance for the specified URL.
*
@@ -112,7 +119,9 @@ public:
* 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);
+ ref <store> getStore
+ (const utility::url& url,
+ ref <security::authenticator> auth = NULL);
/** Properties for the session and for the services.
*/
diff --git a/vmime/net/simpleAuthenticator.hpp b/vmime/net/simpleAuthenticator.hpp
deleted file mode 100644
index 31a90a42..00000000
--- a/vmime/net/simpleAuthenticator.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// 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);
-
- const authenticationInfos requestAuthInfos() const;
-
-private:
-
- string m_username;
- string m_password;
-};
-
-
-} // net
-} // vmime
-
-
-#endif // VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
diff --git a/vmime/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp
index 1743ee7c..2ff67375 100644
--- a/vmime/net/smtp/SMTPTransport.hpp
+++ b/vmime/net/smtp/SMTPTransport.hpp
@@ -40,7 +40,7 @@ class SMTPTransport : public transport
{
public:
- SMTPTransport(ref <session> sess, ref <authenticator> auth);
+ SMTPTransport(ref <session> sess, ref <security::authenticator> auth);
~SMTPTransport();
const string getProtocolName() const;
@@ -58,18 +58,30 @@ public:
private:
- static const int responseCode(const string& response);
- static const string responseText(const string& response);
+ static const int getResponseCode(const string& response);
void sendRequest(const string& buffer, const bool end = true);
- void readResponse(string& buffer);
+ const string readResponseLine();
+ const int readResponse(string& text);
+ const int readAllResponses(string& text, const bool allText = false);
void internalDisconnect();
+ void authenticate();
+#if VMIME_HAVE_SASL_SUPPORT
+ void authenticateSASL();
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+
ref <socket> m_socket;
bool m_authentified;
+
bool m_extendedSMTP;
+ string m_extendedSMTPResponse;
+
+ string m_responseBuffer;
+ bool m_responseContinues;
ref <timeoutHandler> m_timeoutHandler;
@@ -83,6 +95,10 @@ private:
{
// SMTP-specific options
serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH;
+#if VMIME_HAVE_SASL_SUPPORT
+ serviceInfos::property PROPERTY_OPTIONS_SASL;
+ serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
+#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;
diff --git a/vmime/net/store.hpp b/vmime/net/store.hpp
index 5855c76d..997da468 100644
--- a/vmime/net/store.hpp
+++ b/vmime/net/store.hpp
@@ -37,7 +37,7 @@ class store : public service
{
protected:
- store(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
+ store(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth)
: service(sess, infos, auth) { }
public:
diff --git a/vmime/net/transport.hpp b/vmime/net/transport.hpp
index f4be446d..11950034 100644
--- a/vmime/net/transport.hpp
+++ b/vmime/net/transport.hpp
@@ -42,7 +42,7 @@ class transport : public service
{
protected:
- transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth);
+ transport(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth);
public:
diff --git a/vmime/security/authenticator.hpp b/vmime/security/authenticator.hpp
new file mode 100644
index 00000000..04522b10
--- /dev/null
+++ b/vmime/security/authenticator.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_SECURITY_AUTHENTICATOR_HPP_INCLUDED
+#define VMIME_SECURITY_AUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+// Forward declarations
+namespace vmime {
+namespace net {
+
+class service;
+
+} // net
+} // vmime
+
+
+namespace vmime {
+namespace security {
+
+
+/** Provides required information for user authentication. The same
+ * information can be requested multiple time (eg. in IMAP, there is a
+ * new connection started each time a folder is open), so the object is
+ * responsible for caching the information to avoid useless interactions
+ * with the user.
+ *
+ * Usually, you should not inherit from this class, but instead from the
+ * more convenient defaultAuthenticator class.
+ *
+ * WARNING: an authenticator should be used with one and ONLY ONE messaging
+ * service at a time.
+ */
+class authenticator : public object
+{
+public:
+
+ /** Return the authentication identity (usually, this
+ * is the username).
+ *
+ * @return username
+ * @throw exceptions::no_auth_information if the information
+ * could not be provided
+ */
+ virtual const string getUsername() const = 0;
+
+ /** Return the password of the authentication identity.
+ *
+ * @return password
+ * @throw exceptions::no_auth_information if the information
+ * could not be provided
+ */
+ virtual const string getPassword() const = 0;
+
+ /** Return the local host name of the machine.
+ *
+ * @return hostname
+ * @throw exceptions::no_auth_information if the information
+ * could not be provided
+ */
+ virtual const string getHostname() const = 0;
+
+ /** Return the anonymous token (usually, this is the user's
+ * email address).
+ *
+ * @return anonymous token
+ * @throw exceptions::no_auth_information if the information
+ * could not be provided
+ */
+ virtual const string getAnonymousToken() const = 0;
+
+ /** Return the registered service name of the application
+ * service (eg: "imap"). This can be used by GSSAPI or DIGEST-MD5
+ * mechanisms with SASL.
+ *
+ * @return service name
+ * @throw exceptions::no_auth_information if the information
+ * could not be provided
+ */
+ virtual const string getServiceName() const = 0;
+
+ /** Called by the messaging service to allow this authenticator to
+ * know which service is currently using it. This is called just
+ * before the service starts the authentication process.
+ *
+ * @param serv messaging service instance
+ */
+ virtual void setService(ref <net::service> serv) = 0;
+};
+
+
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_AUTHENTICATOR_HPP_INCLUDED
+
diff --git a/vmime/net/defaultAuthenticator.hpp b/vmime/security/defaultAuthenticator.hpp
index a9e58e94..73f65cdc 100644
--- a/vmime/net/defaultAuthenticator.hpp
+++ b/vmime/security/defaultAuthenticator.hpp
@@ -17,44 +17,45 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
-#ifndef VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
-#define VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+#ifndef VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+#define VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED
-#include "vmime/net/authenticator.hpp"
-#include "vmime/propertySet.hpp"
+#include "vmime/security/authenticator.hpp"
namespace vmime {
-namespace net {
+namespace security {
-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.
+/** An authenticator that can provide some basic information by
+ * reading in the messaging session properties.
*/
-
class defaultAuthenticator : public authenticator
{
public:
- defaultAuthenticator(weak_ref <session> session, const string& prefix);
+ defaultAuthenticator();
+ ~defaultAuthenticator();
- const authenticationInfos requestAuthInfos() const;
+ const string getUsername() const;
+ const string getPassword() const;
+ const string getHostname() const;
+ const string getAnonymousToken() const;
+ const string getServiceName() const;
+
+ void setService(ref <net::service> serv);
+ weak_ref <net::service> getService() const;
private:
- weak_ref <session> m_session;
- const string m_prefix;
+ weak_ref <net::service> m_service;
};
-} // net
+} // security
} // vmime
-#endif // VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+#endif // VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/SASLAuthenticator.hpp b/vmime/security/sasl/SASLAuthenticator.hpp
new file mode 100644
index 00000000..fc833f36
--- /dev/null
+++ b/vmime/security/sasl/SASLAuthenticator.hpp
@@ -0,0 +1,84 @@
+//
+// 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_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+#include "vmime/security/authenticator.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+class SASLMechanism;
+class SASLSession;
+
+
+/** SASL-aware authenticator.
+ *
+ * Usually, you should not inherit from this class, but instead from the
+ * more convenient defaultSASLAuthenticator class.
+ */
+class SASLAuthenticator : public authenticator
+{
+public:
+
+ /** This method is called to allow the client to choose the
+ * authentication mechanisms that will be used. By default,
+ * the more secure mechanisms are chosen.
+ *
+ * @param available available mechanisms
+ * @param suggested suggested mechanism (or NULL if the system
+ * could not suggest a mechanism)
+ * @return ordered list of mechanism to use among the available
+ * mechanisms (from the first to try to the last)
+ */
+ virtual const std::vector <ref <SASLMechanism> > getAcceptableMechanisms
+ (const std::vector <ref <SASLMechanism> >& available,
+ ref <SASLMechanism> suggested) const = 0;
+
+ /** Set the SASL session which is using this authenticator.
+ *
+ * @param sess SASL session
+ */
+ virtual void setSASLSession(ref <SASLSession> sess) = 0;
+
+ /** Set the SASL mechanism which has been selected for the
+ * SASL authentication process. This may be called several times
+ * if the multiple mechanisms are tried by the service which
+ * use this authentication.
+ *
+ * @param mech SASL mechanism
+ */
+ virtual void setSASLMechanism(ref <SASLMechanism> mech) = 0;
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/SASLContext.hpp b/vmime/security/sasl/SASLContext.hpp
new file mode 100644
index 00000000..1a71ed76
--- /dev/null
+++ b/vmime/security/sasl/SASLContext.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_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+#include "vmime/security/sasl/SASLSession.hpp"
+#include "vmime/security/sasl/SASLMechanismFactory.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+/** An SASL client context.
+ */
+class SASLContext : public object
+{
+ friend class SASLSession;
+ friend class builtinSASLMechanism;
+
+public:
+
+ ~SASLContext();
+
+ /** Construct and initialize a new SASL context.
+ */
+ SASLContext();
+
+ /** Create and initialize a new SASL session.
+ *
+ * @param serviceName name of the service which will use the session
+ * @param auth authenticator object to use during the session
+ * @param mech SASL mechanism
+ * @return a new SASL session
+ */
+ ref <SASLSession> createSession
+ (const string& serviceName,
+ ref <authenticator> auth, ref <SASLMechanism> mech);
+
+ /** Create an instance of an SASL mechanism.
+ *
+ * @param name mechanism name
+ * @return a new instance of the specified SASL mechanism
+ * @throw exceptions::no_such_mechanism if no mechanism is
+ * registered for the specified name
+ */
+ ref <SASLMechanism> createMechanism(const string& name);
+
+ /** Suggests an SASL mechanism among a set of mechanisms
+ * supported by the server.
+ *
+ * @param mechs list of mechanisms
+ * @return suggested mechanism (usually the safest mechanism
+ * supported by both the client and the server)
+ */
+ ref <SASLMechanism> suggestMechanism
+ (const std::vector <ref <SASLMechanism> >& mechs);
+
+ /** Helper function for decoding Base64-encoded challenge.
+ *
+ * @param input input buffer
+ * @param output output buffer
+ * @param outputLen length of output buffer
+ */
+ void decodeB64(const string& input, byte** output, int* outputLen);
+
+ /** Helper function for encoding challenge in Base64.
+ *
+ * @param input input buffer
+ * @param inputLen length of input buffer
+ * @return Base64-encoded challenge
+ */
+ const string encodeB64(const byte* input, const int inputLen);
+
+private:
+
+ static const string getErrorMessage(const string& fname, const int code);
+
+
+#ifdef GSASL_VERSION
+ Gsasl* m_gsaslContext;
+#else
+ void* m_gsaslContext;
+#endif // GSASL_VERSION
+
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/SASLMechanism.hpp b/vmime/security/sasl/SASLMechanism.hpp
new file mode 100644
index 00000000..366198e7
--- /dev/null
+++ b/vmime/security/sasl/SASLMechanism.hpp
@@ -0,0 +1,119 @@
+//
+// 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_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+class SASLSession;
+
+
+/** An SASL mechanism.
+ */
+class SASLMechanism : public object
+{
+public:
+
+ /** Return the name of this mechanism.
+ *
+ * @return mechanism name
+ */
+ virtual const string getName() const = 0;
+
+ /** Perform one step of SASL authentication. Accept data from the
+ * server (challenge), process it and return data to be returned
+ * in response to the server.
+ *
+ * @param sess SASL session
+ * @param challenge challenge sent from the server
+ * @param challengeLen length of challenge
+ * @param response response to send to the server (allocated by
+ * this function, free with delete[])
+ * @param responseLen length of response buffer
+ * @return true if authentication terminated successfully, or
+ * false if the authentication process should continue
+ * @throw exceptions::sasl_exception if an error occured during
+ * authentication (in this case, the values in 'response' and
+ * 'responseLen' are undetermined)
+ */
+ virtual const bool step
+ (ref <SASLSession> sess,
+ const byte* challenge, const int challengeLen,
+ byte** response, int* responseLen) = 0;
+
+ /** Check whether authentication has completed. If false, more
+ * calls to evaluateChallenge() are needed to complete the
+ * authentication process).
+ *
+ * @return true if the authentication has finished, or false
+ * otherwise
+ */
+ virtual const bool isComplete() const = 0;
+
+ /** Encode data according to negotiated SASL mechanism. This
+ * might mean that data is integrity or privacy protected.
+ *
+ * @param sess SASL session
+ * @param input input buffer
+ * @param inputLen length of input buffer
+ * @param output output buffer (allocated bu the function,
+ * free with delete[])
+ * @param outputLen length of output buffer
+ * @throw exceptions::sasl_exception if an error occured during
+ * the encoding of data (in this case, the values in 'output' and
+ * 'outputLen' are undetermined)
+ */
+ virtual void encode(ref <SASLSession> sess,
+ const byte* input, const int inputLen,
+ byte** output, int* outputLen) = 0;
+
+ /** Decode data according to negotiated SASL mechanism. This
+ * might mean that data is integrity or privacy protected.
+ *
+ * @param sess SASL session
+ * @param input input buffer
+ * @param inputLen length of input buffer
+ * @param output output buffer (allocated bu the function,
+ * free with delete[])
+ * @param outputLen length of output buffer
+ * @throw exceptions::sasl_exception if an error occured during
+ * the encoding of data (in this case, the values in 'output' and
+ * 'outputLen' are undetermined)
+ */
+ virtual void decode(ref <SASLSession> sess,
+ const byte* input, const int inputLen,
+ byte** output, int* outputLen) = 0;
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/SASLMechanismFactory.hpp b/vmime/security/sasl/SASLMechanismFactory.hpp
new file mode 100644
index 00000000..5cf0b3c7
--- /dev/null
+++ b/vmime/security/sasl/SASLMechanismFactory.hpp
@@ -0,0 +1,131 @@
+//
+// 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_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+#include "vmime/base.hpp"
+
+#include "vmime/security/sasl/SASLMechanism.hpp"
+
+#include <map>
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+class SASLContext;
+
+
+/** Constructs SASL mechanism objects.
+ */
+class SASLMechanismFactory : public object
+{
+private:
+
+ SASLMechanismFactory();
+ ~SASLMechanismFactory();
+
+
+ class registeredMechanism : public object
+ {
+ public:
+
+ virtual ref <SASLMechanism> create
+ (ref <SASLContext> ctx, const string& name) = 0;
+ };
+
+ template <typename T>
+ class registeredMechanismImpl : public registeredMechanism
+ {
+ public:
+
+ ref <SASLMechanism> create(ref <SASLContext> ctx, const string& name)
+ {
+ return vmime::create <T>(ctx, name);
+ }
+ };
+
+ typedef std::map <string, ref <registeredMechanism> > MapType;
+ MapType m_mechs;
+
+public:
+
+ static SASLMechanismFactory* getInstance();
+
+ /** Register a mechanism into this factory, so that subsequent
+ * calls to create return a valid object for this mechanism.
+ *
+ * @param name mechanism name
+ */
+ template <typename MECH_CLASS>
+ void registerMechanism(const string& name)
+ {
+ m_mechs.insert(MapType::value_type(name,
+ vmime::create <registeredMechanismImpl <MECH_CLASS> >()));
+ }
+
+ /** Create a mechanism object given its name.
+ *
+ * @param ctx SASL context
+ * @param name mechanism name
+ * @return a new mechanism object
+ * @throw exceptions::no_such_mechanism if no mechanism is
+ * registered for the specified name
+ */
+ ref <SASLMechanism> create(ref <SASLContext> ctx, const string& name);
+
+ /** Return a list of supported mechanisms. This includes mechanisms
+ * registered using registerMechanism() as well as the ones that
+ * are built-in.
+ *
+ * @return list of supported mechanisms
+ */
+ const std::vector <string> getSupportedMechanisms() const;
+
+ /** Test whether an authentication mechanism is supported.
+ *
+ * @param name mechanism name
+ * @return true if the specified mechanism is supported,
+ * false otherwise
+ */
+ const bool isMechanismSupported(const string& name) const;
+
+private:
+
+#ifdef GSASL_VERSION
+ Gsasl* m_gsaslContext;
+#else
+ void* m_gsaslContext;
+#endif // GSASL_VERSION
+
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/SASLSession.hpp b/vmime/security/sasl/SASLSession.hpp
new file mode 100644
index 00000000..0a76caa3
--- /dev/null
+++ b/vmime/security/sasl/SASLSession.hpp
@@ -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.
+//
+
+#ifndef VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED
+
+
+#include "vmime/types.hpp"
+
+#include "vmime/security/sasl/SASLAuthenticator.hpp"
+#include "vmime/security/sasl/SASLMechanism.hpp"
+#include "vmime/security/sasl/SASLSocket.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+class SASLContext;
+
+
+/** An SASL client session.
+ */
+class SASLSession : public object
+{
+ friend class builtinSASLMechanism;
+ friend class SASLSocket;
+
+public:
+
+ ~SASLSession();
+
+ /** Construct a new SASL session.
+ *
+ * @param serviceName name of the service using this session
+ * @param ctx SASL context
+ * @param auth authenticator to use for this session
+ * @param mech SASL mechanism
+ */
+ SASLSession(const string& serviceName, ref <SASLContext> ctx,
+ ref <authenticator> auth, ref <SASLMechanism> mech);
+
+ /** Initialize this SASL session. This must be called before
+ * calling any other method on this object (except accessors).
+ */
+ void init();
+
+ /** Return the authenticator used for this session. This is the
+ * authenticator which has been previously set with a call to
+ * setAuthenticator().
+ *
+ * @return authenticator object
+ */
+ ref <authenticator> getAuthenticator();
+
+ /** Return the mechanism used for this session.
+ *
+ * @return SASL mechanism
+ */
+ ref <SASLMechanism> getMechanism();
+
+ /** Return the SASL context.
+ *
+ * @return SASL context
+ */
+ ref <SASLContext> getContext();
+
+ /** Perform one step of SASL authentication. Accept data from the
+ * server (challenge), process it and return data to be returned
+ * in response to the server.
+ *
+ * @param challenge challenge sent from the server
+ * @param challengeLen length of challenge
+ * @param response response to send to the server (allocated by
+ * this function, free with delete[])
+ * @param responseLen length of response buffer
+ * @return true if authentication terminated successfully, or
+ * false if the authentication process should continue
+ * @throw exceptions::sasl_exception if an error occured during
+ * authentication (in this case, the values in 'response' and
+ * 'responseLen' are undetermined)
+ */
+ const bool evaluateChallenge
+ (const byte* challenge, const int challengeLen,
+ byte** response, int* responseLen);
+
+ /** Return a socket in which transmitted data is integrity
+ * and/or privacy protected, depending on the QOP (Quality of
+ * Protection) negotiated during the SASL authentication.
+ *
+ * @param sok socket to wrap
+ * @return secured socket
+ */
+ ref <net::socket> getSecuredSocket(ref <net::socket> sok);
+
+ /** Return the name of the service which is using this
+ * SASL session (eg. "imap"). This value should be returned
+ * by the authenticator when INFO_SERVICE is requested.
+ *
+ * @return service name
+ */
+ const string getServiceName() const;
+
+private:
+
+ const string m_serviceName;
+
+ ref <SASLContext> m_context;
+ ref <authenticator> m_auth;
+ ref <SASLMechanism> m_mech;
+
+#ifdef GSASL_VERSION
+ Gsasl* m_gsaslContext;
+ Gsasl_session* m_gsaslSession;
+
+ static int gsaslCallback(Gsasl* ctx, Gsasl_session* sctx, Gsasl_property prop);
+#else
+ void* m_gsaslContext;
+ void* m_gsaslSession;
+
+ static int gsaslCallback(void* ctx, void* sctx, int prop);
+#endif // GSASL_VERSION
+
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED
+
diff --git a/vmime/net/authenticationInfos.hpp b/vmime/security/sasl/SASLSocket.hpp
index 2bc394fd..e29415cd 100644
--- a/vmime/net/authenticationInfos.hpp
+++ b/vmime/security/sasl/SASLSocket.hpp
@@ -17,48 +17,60 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
-#ifndef VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
-#define VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
+#ifndef VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED
#include "vmime/types.hpp"
+#include "vmime/net/socket.hpp"
+
namespace vmime {
-namespace net {
+namespace security {
+namespace sasl {
-/** This class encapsulates user credentials.
- */
+class SASLSession;
+
-class authenticationInfos : public object
+/** A socket which provides data integrity and/or privacy protection.
+ */
+class SASLSocket : public net::socket
{
public:
- authenticationInfos(const string& username, const string& password);
- authenticationInfos(const authenticationInfos& infos);
+ SASLSocket(ref <SASLSession> sess, ref <net::socket> wrapped);
+ ~SASLSocket();
+
+ void connect(const string& address, const port_t port);
+ void disconnect();
- /** Return the user account name.
- *
- * @return account name
- */
- const string& getUsername() const;
+ const bool isConnected() const;
- /** Return the user account password.
- *
- * @return account password
- */
- const string& getPassword() const;
+ void receive(string& buffer);
+ const int receiveRaw(char* buffer, const int count);
+
+ void send(const string& buffer);
+ void sendRaw(const char* buffer, const int count);
private:
- string m_username;
- string m_password;
+ ref <SASLSession> m_session;
+ ref <net::socket> m_wrapped;
+
+ byte* m_pendingBuffer;
+ int m_pendingPos;
+ int m_pendingLen;
+
+ char m_recvBuffer[65536];
};
-} // net
+} // sasl
+} // security
} // vmime
-#endif // VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
+#endif // VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/builtinSASLMechanism.hpp b/vmime/security/sasl/builtinSASLMechanism.hpp
new file mode 100644
index 00000000..4a26a922
--- /dev/null
+++ b/vmime/security/sasl/builtinSASLMechanism.hpp
@@ -0,0 +1,82 @@
+//
+// 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_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED
+
+
+#include "vmime/security/sasl/SASLMechanism.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+class SASLContext;
+
+
+/** A built-in authentication mechanism that relies on
+ * the GNU SASL library.
+ */
+class builtinSASLMechanism : public SASLMechanism
+{
+public:
+
+ builtinSASLMechanism(ref <SASLContext> ctx, const string& name);
+ ~builtinSASLMechanism();
+
+
+ const string getName() const;
+
+ const bool step
+ (ref <SASLSession> sess,
+ const byte* challenge, const int challengeLen,
+ byte** response, int* responseLen);
+
+ const bool isComplete() const;
+
+ void encode(ref <SASLSession> sess,
+ const byte* input, const int inputLen,
+ byte** output, int* outputLen);
+
+ void decode(ref <SASLSession> sess,
+ const byte* input, const int inputLen,
+ byte** output, int* outputLen);
+
+private:
+
+ /** SASL context */
+ ref <SASLContext> m_context;
+
+ /** Mechanism name */
+ const string m_name;
+
+ /** Authentication process status. */
+ bool m_complete;
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED
+
diff --git a/vmime/security/sasl/defaultSASLAuthenticator.hpp b/vmime/security/sasl/defaultSASLAuthenticator.hpp
new file mode 100644
index 00000000..9011b779
--- /dev/null
+++ b/vmime/security/sasl/defaultSASLAuthenticator.hpp
@@ -0,0 +1,80 @@
+//
+// 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_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED
+#define VMIME_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED
+
+
+#include "vmime/security/sasl/SASLAuthenticator.hpp"
+#include "vmime/security/defaultAuthenticator.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace sasl {
+
+
+/** An authenticator that is capable of providing information
+ * for simple authentication mechanisms (username and password).
+ */
+class defaultSASLAuthenticator : public SASLAuthenticator
+{
+public:
+
+ defaultSASLAuthenticator();
+ ~defaultSASLAuthenticator();
+
+ const std::vector <ref <SASLMechanism> > getAcceptableMechanisms
+ (const std::vector <ref <SASLMechanism> >& available,
+ ref <SASLMechanism> suggested) const;
+
+ const string getUsername() const;
+ const string getPassword() const;
+ const string getHostname() const;
+ const string getAnonymousToken() const;
+ const string getServiceName() const;
+
+ void setService(ref <net::service> serv);
+ weak_ref <net::service> getService() const;
+
+ void setSASLSession(ref <SASLSession> sess);
+ ref <SASLSession> getSASLSession() const;
+
+ void setSASLMechanism(ref <SASLMechanism> mech);
+ ref <SASLMechanism> getSASLMechanism() const;
+
+private:
+
+ defaultAuthenticator m_default;
+
+
+ weak_ref <net::service> m_service;
+
+ ref <SASLSession> m_saslSession;
+ ref <SASLMechanism> m_saslMech;
+};
+
+
+} // sasl
+} // security
+} // vmime
+
+
+#endif // VMIME_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED
+
diff --git a/vmime/types.hpp b/vmime/types.hpp
index 4e2fa923..47cc8507 100644
--- a/vmime/types.hpp
+++ b/vmime/types.hpp
@@ -23,6 +23,7 @@
#include <limits>
#include <string>
+#include <vector>
#include "vmime/config.hpp"
#include "vmime/utility/smartPtr.hpp"
@@ -40,6 +41,7 @@ namespace vmime
typedef int char_t;
typedef vmime_uint8 byte;
+ typedef std::vector <byte> byteArray;
// Some aliases
namespace utils = utility;
diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp
index 4b1cba2d..754f65a3 100644
--- a/vmime/utility/stream.hpp
+++ b/vmime/utility/stream.hpp
@@ -326,6 +326,29 @@ private:
};
+/** An adapter class for reading from an array of bytes.
+ */
+
+class inputStreamByteBufferAdapter : public inputStream
+{
+public:
+
+ inputStreamByteBufferAdapter(const byte* buffer, size_type length);
+
+ const bool eof() const;
+ void reset();
+ const size_type read(value_type* const data, const size_type count);
+ const size_type skip(const size_type count);
+
+private:
+
+ const byte* m_buffer;
+ const size_type m_length;
+
+ size_type m_pos;
+};
+
+
#if VMIME_HAVE_MESSAGING_FEATURES
diff --git a/vmime/vmime.hpp b/vmime/vmime.hpp
index 3a6b6474..ae44d963 100644
--- a/vmime/vmime.hpp
+++ b/vmime/vmime.hpp
@@ -86,6 +86,21 @@
#include "vmime/utility/datetimeUtils.hpp"
#include "vmime/utility/filteredStream.hpp"
+// Security
+#include "vmime/security/authenticator.hpp"
+#include "vmime/security/defaultAuthenticator.hpp"
+
+// Security/digest
+#include "vmime/security/digest/messageDigestFactory.hpp"
+
+// Security/SASL
+#if VMIME_HAVE_SASL_SUPPORT
+ #include "vmime/security/sasl/SASLAuthenticator.hpp"
+ #include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
+ #include "vmime/security/sasl/SASLContext.hpp"
+ #include "vmime/security/sasl/SASLSession.hpp"
+#endif // VMIME_HAVE_SASL_SUPPORT
+
// Messaging features
#if VMIME_HAVE_MESSAGING_FEATURES
#include "vmime/net/socket.hpp"
@@ -95,9 +110,6 @@
#include "vmime/net/transport.hpp"
#include "vmime/net/session.hpp"
- #include "vmime/net/authenticator.hpp"
- #include "vmime/net/defaultAuthenticator.hpp"
- #include "vmime/net/simpleAuthenticator.hpp"
#include "vmime/net/folder.hpp"
#include "vmime/net/message.hpp"