aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/exception.cpp54
-rw-r--r--src/net/authHelper.cpp111
-rw-r--r--src/net/builtinServices.inl30
-rw-r--r--src/net/imap/IMAPConnection.cpp112
-rw-r--r--src/net/imap/IMAPSStore.cpp65
-rw-r--r--src/net/imap/IMAPServiceInfos.cpp133
-rw-r--r--src/net/imap/IMAPStore.cpp73
-rw-r--r--src/net/maildir/maildirServiceInfos.cpp64
-rw-r--r--src/net/maildir/maildirStore.cpp42
-rw-r--r--src/net/pop3/POP3SStore.cpp66
-rw-r--r--src/net/pop3/POP3ServiceInfos.cpp139
-rw-r--r--src/net/pop3/POP3Store.cpp191
-rw-r--r--src/net/sendmail/sendmailServiceInfos.cpp65
-rw-r--r--src/net/sendmail/sendmailTransport.cpp41
-rw-r--r--src/net/service.cpp25
-rw-r--r--src/net/serviceInfos.cpp10
-rw-r--r--src/net/smtp/SMTPSTransport.cpp66
-rw-r--r--src/net/smtp/SMTPServiceInfos.cpp136
-rw-r--r--src/net/smtp/SMTPTransport.cpp169
-rw-r--r--src/net/tls/TLSSession.cpp342
-rw-r--r--src/net/tls/TLSSocket.cpp391
-rw-r--r--src/net/tls/X509Certificate.cpp273
-rw-r--r--src/net/tls/certificateChain.cpp (renamed from vmime/net/authHelper.hpp)27
-rw-r--r--src/net/tls/defaultCertificateVerifier.cpp164
-rw-r--r--src/utility/stream.cpp16
25 files changed, 2409 insertions, 396 deletions
diff --git a/src/exception.cpp b/src/exception.cpp
index f2106e89..9620a310 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -719,6 +719,60 @@ const char* no_auth_information::name() const throw() { return "no_auth_informat
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+
+
+//
+// tls_exception
+//
+
+tls_exception::~tls_exception() throw() {}
+tls_exception::tls_exception(const string& what, const exception& other)
+ : exception(what, other) {}
+
+exception* tls_exception::clone() const { return new tls_exception(*this); }
+const char* tls_exception::name() const throw() { return "tls_exception"; }
+
+
+//
+// certificate_exception
+//
+
+certificate_exception::~certificate_exception() throw() {}
+certificate_exception::certificate_exception(const string& what, const exception& other)
+ : tls_exception(what, other) {}
+
+exception* certificate_exception::clone() const { return new certificate_exception(*this); }
+const char* certificate_exception::name() const throw() { return "certificate_exception"; }
+
+
+//
+// certificate_verification_exception
+//
+
+certificate_verification_exception::~certificate_verification_exception() throw() {}
+certificate_verification_exception::certificate_verification_exception(const string& what, const exception& other)
+ : certificate_exception(what, other) {}
+
+exception* certificate_verification_exception::clone() const { return new certificate_verification_exception(*this); }
+const char* certificate_verification_exception::name() const throw() { return "certificate_verification_exception"; }
+
+
+//
+// unsupported_certificate_type
+//
+
+unsupported_certificate_type::~unsupported_certificate_type() throw() {}
+unsupported_certificate_type::unsupported_certificate_type(const string& type, const exception& other)
+ : certificate_exception("Unsupported certificate type: '" + type + "'", other) {}
+
+exception* unsupported_certificate_type::clone() const { return new unsupported_certificate_type(*this); }
+const char* unsupported_certificate_type::name() const throw() { return "unsupported_certificate_type"; }
+
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+
} // exceptions
diff --git a/src/net/authHelper.cpp b/src/net/authHelper.cpp
deleted file mode 100644
index e9c217f3..00000000
--- a/src/net/authHelper.cpp
+++ /dev/null
@@ -1,111 +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 along
-// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
-//
-
-#include "vmime/net/authHelper.hpp"
-
-#include "vmime/config.hpp"
-#include "vmime/security/digest/messageDigestFactory.hpp"
-
-
-namespace vmime {
-namespace net {
-
-
-//
-// This code is based on the Sample Code published in the Appendix of
-// the RFC-2104: "HMAC: Keyed-Hashing for Message Authentication".
-//
-
-void hmac_md5(const string& text, const string& key, string& hexDigest)
-{
- vmime_uint8 digest[16];
-
- unsigned char ipad[65]; // inner padding - key XORd with ipad
- unsigned char opad[65]; // outer padding - key XORd with opad
-
- unsigned char tkey[16];
- int tkeyLen;
-
- ref <security::digest::messageDigest> md5 =
- security::digest::messageDigestFactory::getInstance()->create("md5");
-
- // If key is longer than 64 bytes reset it to key = MD5(key)
- if (key.length() > 64)
- {
- md5->reset();
- md5->update(reinterpret_cast <const vmime_uint8*>(key.data()), key.length());
- md5->finalize();
-
- std::copy(md5->getDigest(), md5->getDigest() + 16, tkey);
- tkeyLen = 16;
- }
- else
- {
- std::copy(key.begin(), key.end(), tkey);
- tkeyLen = key.length();
- }
-
- //
- // the HMAC_MD5 transform looks like:
- //
- // MD5(K XOR opad, MD5(K XOR ipad, text))
- //
- // where K is an n byte key
- // ipad is the byte 0x36 repeated 64 times
- //
- // opad is the byte 0x5c repeated 64 times
- // and text is the data being protected
- //
-
- // Start out by storing key in pads
- std::fill(ipad, ipad + sizeof(ipad), 0);
- std::fill(opad, opad + sizeof(opad), 0);
-
- std::copy(tkey, tkey + tkeyLen, ipad);
- std::copy(tkey, tkey + tkeyLen, opad);
-
- // XOR key with ipad and opad values
- for (int i = 0 ; i < 64 ; ++i)
- {
- ipad[i] ^= 0x36;
- opad[i] ^= 0x5c;
- }
-
- // Perform inner MD5
- md5->reset();
- md5->update(ipad, 64);
- md5->update(text);
- md5->finalize();
-
- std::copy(md5->getDigest(), md5->getDigest() + 16, digest);
-
- // Perform outer MD5
- md5->reset();
- md5->update(opad, 64);
- md5->update(digest, 16);
- md5->finalize();
-
- //std::copy(outerMD5.hash(), outerMD5.hash() + 16, digest);
-
- hexDigest = md5->getHexDigest();
-}
-
-
-} // net
-} // vmime
diff --git a/src/net/builtinServices.inl b/src/net/builtinServices.inl
index 88578e4b..803371ac 100644
--- a/src/net/builtinServices.inl
+++ b/src/net/builtinServices.inl
@@ -24,36 +24,52 @@
#ifndef VMIME_BUILDING_DOC
-#define REGISTER_SERVICE(p_class, p_name) \
- vmime::net::service::initializer <vmime::net::p_class> p_name(#p_name)
+#define REGISTER_SERVICE(p_class, p_name, p_type) \
+ vmime::net::service::initializer <vmime::net::p_class> \
+ p_name(#p_name, vmime::net::service::p_type)
#if VMIME_BUILTIN_MESSAGING_PROTO_POP3
#include "vmime/net/pop3/POP3Store.hpp"
- REGISTER_SERVICE(pop3::POP3Store, pop3);
+ REGISTER_SERVICE(pop3::POP3Store, pop3, TYPE_STORE);
+
+ #if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/pop3/POP3SStore.hpp"
+ REGISTER_SERVICE(pop3::POP3SStore, pop3s, TYPE_STORE);
+ #endif // VMIME_HAVE_TLS_SUPPORT
#endif
#if VMIME_BUILTIN_MESSAGING_PROTO_SMTP
#include "vmime/net/smtp/SMTPTransport.hpp"
- REGISTER_SERVICE(smtp::SMTPTransport, smtp);
+ REGISTER_SERVICE(smtp::SMTPTransport, smtp, TYPE_TRANSPORT);
+
+ #if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/smtp/SMTPSTransport.hpp"
+ REGISTER_SERVICE(smtp::SMTPSTransport, smtps, TYPE_TRANSPORT);
+ #endif // VMIME_HAVE_TLS_SUPPORT
#endif
#if VMIME_BUILTIN_MESSAGING_PROTO_IMAP
#include "vmime/net/imap/IMAPStore.hpp"
- REGISTER_SERVICE(imap::IMAPStore, imap);
+ REGISTER_SERVICE(imap::IMAPStore, imap, TYPE_STORE);
+
+ #if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/imap/IMAPSStore.hpp"
+ REGISTER_SERVICE(imap::IMAPSStore, imaps, TYPE_STORE);
+ #endif // VMIME_HAVE_TLS_SUPPORT
#endif
#if VMIME_BUILTIN_MESSAGING_PROTO_MAILDIR
#include "vmime/net/maildir/maildirStore.hpp"
- REGISTER_SERVICE(maildir::maildirStore, maildir);
+ REGISTER_SERVICE(maildir::maildirStore, maildir, TYPE_STORE);
#endif
#if VMIME_BUILTIN_MESSAGING_PROTO_SENDMAIL
#include "vmime/net/sendmail/sendmailTransport.hpp"
- REGISTER_SERVICE(sendmail::sendmailTransport, sendmail);
+ REGISTER_SERVICE(sendmail::sendmailTransport, sendmail, TYPE_TRANSPORT);
#endif
diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp
index 6d2a4a4e..b0716f45 100644
--- a/src/net/imap/IMAPConnection.cpp
+++ b/src/net/imap/IMAPConnection.cpp
@@ -29,16 +29,20 @@
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/tls/TLSSession.hpp"
+#endif // VMIME_HAVE_TLS_SUPPORT
+
#include <sstream>
// Helpers for service properties
#define GET_PROPERTY(type, prop) \
(m_store->getInfos().getPropertyValue <type>(getSession(), \
- dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+ dynamic_cast <const IMAPServiceInfos&>(m_store->getInfos()).getProperties().prop))
#define HAS_PROPERTY(prop) \
(m_store->getInfos().hasProperty(getSession(), \
- dynamic_cast <const IMAPStore::_infos&>(m_store->getInfos()).getProperties().prop))
+ dynamic_cast <const IMAPServiceInfos&>(m_store->getInfos()).getProperties().prop))
namespace vmime {
@@ -94,6 +98,20 @@ void IMAPConnection::connect()
getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
m_socket = sf->create();
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (m_store->isSecuredConnection()) // dedicated port/IMAPS
+ {
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(m_store->getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ m_socket = tlsSocket;
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
m_socket->connect(address, port);
@@ -110,6 +128,7 @@ void IMAPConnection::connect()
// --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready
utility::auto_ptr <IMAPParser::greeting> greet(m_parser->readGreeting());
+ bool needAuth = false;
if (greet->resp_cond_bye())
{
@@ -118,6 +137,47 @@ void IMAPConnection::connect()
}
else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH)
{
+ needAuth = true;
+ }
+
+#if VMIME_HAVE_TLS_SUPPORT
+ // Setup secured connection, if requested
+ const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS);
+ const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED);
+
+ if (!m_store->isSecuredConnection() && tls) // only if not IMAPS
+ {
+ try
+ {
+ startTLS();
+ }
+ // Non-fatal error
+ catch (exceptions::command_error&)
+ {
+ if (tlsRequired)
+ {
+ m_state = STATE_NONE;
+ throw;
+ }
+ else
+ {
+ // TLS is not required, so don't bother
+ }
+ }
+ // Fatal error
+ catch (...)
+ {
+ m_state = STATE_NONE;
+ throw;
+ }
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ // Authentication
+ if (needAuth)
+ {
try
{
authenticate();
@@ -287,17 +347,19 @@ void IMAPConnection::authenticateSASL()
respDataList = resp->continue_req_or_response_data();
string response;
+ bool hasResponse = false;
for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
{
if (respDataList[i]->continue_req())
{
response = respDataList[i]->continue_req()->resp_text()->text();
+ hasResponse = true;
break;
}
}
- if (response.empty())
+ if (!hasResponse)
{
cont = false;
continue;
@@ -365,6 +427,50 @@ void IMAPConnection::authenticateSASL()
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+
+void IMAPConnection::startTLS()
+{
+ try
+ {
+ send(true, "STARTTLS", true);
+
+ utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
+
+ if (resp->isBad() || resp->response_done()->response_tagged()->
+ resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
+ {
+ throw exceptions::command_error
+ ("STARTTLS", m_parser->lastLine(), "bad response");
+ }
+
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(m_store->getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ tlsSocket->handshake(m_timeoutHandler);
+
+ m_socket = tlsSocket;
+ m_parser->setSocket(m_socket);
+ }
+ catch (exceptions::command_error&)
+ {
+ // Non-fatal error
+ throw;
+ }
+ catch (exception&)
+ {
+ // Fatal error
+ internalDisconnect();
+ throw;
+ }
+}
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+
const std::vector <string> IMAPConnection::getCapabilities()
{
send(true, "CAPABILITY", true);
diff --git a/src/net/imap/IMAPSStore.cpp b/src/net/imap/IMAPSStore.cpp
new file mode 100644
index 00000000..ed637c43
--- /dev/null
+++ b/src/net/imap/IMAPSStore.cpp
@@ -0,0 +1,65 @@
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/imap/IMAPSStore.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPSStore::IMAPSStore(ref <session> sess, ref <security::authenticator> auth)
+ : IMAPStore(sess, auth, true)
+{
+}
+
+
+IMAPSStore::~IMAPSStore()
+{
+}
+
+
+const string IMAPSStore::getProtocolName() const
+{
+ return "imaps";
+}
+
+
+
+// Service infos
+
+IMAPServiceInfos IMAPSStore::sm_infos(true);
+
+
+const serviceInfos& IMAPSStore::getInfosInstance()
+{
+ return sm_infos;
+}
+
+
+const serviceInfos& IMAPSStore::getInfos() const
+{
+ return sm_infos;
+}
+
+
+} // imap
+} // net
+} // vmime
diff --git a/src/net/imap/IMAPServiceInfos.cpp b/src/net/imap/IMAPServiceInfos.cpp
new file mode 100644
index 00000000..d8164ce1
--- /dev/null
+++ b/src/net/imap/IMAPServiceInfos.cpp
@@ -0,0 +1,133 @@
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/imap/IMAPServiceInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace imap {
+
+
+IMAPServiceInfos::IMAPServiceInfos(const bool imaps)
+ : m_imaps(imaps)
+{
+}
+
+
+const string IMAPServiceInfos::getPropertyPrefix() const
+{
+ if (m_imaps)
+ return "store.imaps.";
+ else
+ return "store.imap.";
+}
+
+
+const IMAPServiceInfos::props& IMAPServiceInfos::getProperties() const
+{
+ static props imapProps =
+ {
+ // IMAP-specific options
+#if VMIME_HAVE_SASL_SUPPORT
+ property("options.sasl", serviceInfos::property::TYPE_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),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "143"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ static props imapsProps =
+ {
+ // IMAP-specific options
+#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),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "993"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return m_imaps ? imapsProps : imapProps;
+}
+
+
+const std::vector <serviceInfos::property> IMAPServiceInfos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // IMAP-specific options
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (!m_imaps)
+ {
+ list.push_back(p.PROPERTY_CONNECTION_TLS);
+ list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED);
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return list;
+}
+
+
+} // imap
+} // net
+} // vmime
+
diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp
index d4f18e7c..95f96276 100644
--- a/src/net/imap/IMAPStore.cpp
+++ b/src/net/imap/IMAPStore.cpp
@@ -32,8 +32,8 @@ namespace net {
namespace imap {
-IMAPStore::IMAPStore(ref <session> sess, ref <security::authenticator> auth)
- : store(sess, getInfosInstance(), auth), m_connection(NULL)
+IMAPStore::IMAPStore(ref <session> sess, ref <security::authenticator> auth, const bool secured)
+ : store(sess, getInfosInstance(), auth), m_connection(NULL), m_secured(secured)
{
}
@@ -91,6 +91,12 @@ const bool IMAPStore::isValidFolderName(const folder::path::component& /* name *
}
+const bool IMAPStore::isSecuredConnection() const
+{
+ return m_secured;
+}
+
+
void IMAPStore::connect()
{
if (isConnected())
@@ -189,78 +195,21 @@ const int IMAPStore::getCapabilities() const
// Service infos
-IMAPStore::_infos IMAPStore::sm_infos;
+IMAPServiceInfos IMAPStore::sm_infos(false);
const serviceInfos& IMAPStore::getInfosInstance()
{
- return (sm_infos);
+ return sm_infos;
}
const serviceInfos& IMAPStore::getInfos() const
{
- return (sm_infos);
-}
-
-
-const string IMAPStore::_infos::getPropertyPrefix() const
-{
- return "store.imap.";
+ return sm_infos;
}
-const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const
-{
- static props p =
- {
- // IMAP-specific options
-#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),
- property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
-
- property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
- property(serviceInfos::property::SERVER_PORT, "143"),
- property(serviceInfos::property::SERVER_SOCKETFACTORY),
-
- property(serviceInfos::property::TIMEOUT_FACTORY)
- };
-
- return p;
-}
-
-
-const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProperties() const
-{
- std::vector <property> list;
- const props& p = getProperties();
-
- // IMAP-specific options
-#if VMIME_HAVE_SASL_SUPPORT
- list.push_back(p.PROPERTY_OPTIONS_SASL);
- list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
-#endif // VMIME_HAVE_SASL_SUPPORT
-
- // Common properties
- list.push_back(p.PROPERTY_AUTH_USERNAME);
- list.push_back(p.PROPERTY_AUTH_PASSWORD);
-
- list.push_back(p.PROPERTY_SERVER_ADDRESS);
- list.push_back(p.PROPERTY_SERVER_PORT);
- list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
-
- list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
-
- return (list);
-}
-
-
-
} // imap
} // net
} // vmime
diff --git a/src/net/maildir/maildirServiceInfos.cpp b/src/net/maildir/maildirServiceInfos.cpp
new file mode 100644
index 00000000..f6487103
--- /dev/null
+++ b/src/net/maildir/maildirServiceInfos.cpp
@@ -0,0 +1,64 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/maildir/maildirServiceInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace maildir {
+
+
+maildirServiceInfos::maildirServiceInfos()
+{
+}
+
+
+const string maildirServiceInfos::getPropertyPrefix() const
+{
+ return "store.maildir.";
+}
+
+
+const maildirServiceInfos::props& maildirServiceInfos::getProperties() const
+{
+ static props maildirProps =
+ {
+ property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED)
+ };
+
+ return maildirProps;
+}
+
+
+const std::vector <serviceInfos::property> maildirServiceInfos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ list.push_back(p.PROPERTY_SERVER_ROOTPATH);
+
+ return list;
+}
+
+
+} // maildir
+} // net
+} // vmime
+
diff --git a/src/net/maildir/maildirStore.cpp b/src/net/maildir/maildirStore.cpp
index bbb95624..d1baf8dd 100644
--- a/src/net/maildir/maildirStore.cpp
+++ b/src/net/maildir/maildirStore.cpp
@@ -29,9 +29,11 @@
// Helpers for service properties
#define GET_PROPERTY(type, prop) \
- (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+ (getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const maildirServiceInfos&>(getInfos()).getProperties().prop))
#define HAS_PROPERTY(prop) \
- (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+ (getInfos().hasProperty(getSession(), \
+ dynamic_cast <const maildirServiceInfos&>(getInfos()).getProperties().prop))
namespace vmime {
@@ -205,50 +207,20 @@ const int maildirStore::getCapabilities() const
-
// Service infos
-maildirStore::_infos maildirStore::sm_infos;
+maildirServiceInfos maildirStore::sm_infos;
const serviceInfos& maildirStore::getInfosInstance()
{
- return (sm_infos);
+ return sm_infos;
}
const serviceInfos& maildirStore::getInfos() const
{
- return (sm_infos);
-}
-
-
-const string maildirStore::_infos::getPropertyPrefix() const
-{
- return "store.maildir.";
-}
-
-
-const maildirStore::_infos::props& maildirStore::_infos::getProperties() const
-{
- static props p =
- {
- property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED)
- };
-
- return p;
-}
-
-
-const std::vector <serviceInfos::property> maildirStore::_infos::getAvailableProperties() const
-{
- std::vector <property> list;
- const props& p = getProperties();
-
- // Maildir-specific properties
- list.push_back(p.PROPERTY_SERVER_ROOTPATH);
-
- return (list);
+ return sm_infos;
}
diff --git a/src/net/pop3/POP3SStore.cpp b/src/net/pop3/POP3SStore.cpp
new file mode 100644
index 00000000..760ddd86
--- /dev/null
+++ b/src/net/pop3/POP3SStore.cpp
@@ -0,0 +1,66 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/pop3/POP3SStore.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3SStore::POP3SStore(ref <session> sess, ref <security::authenticator> auth)
+ : POP3Store(sess, auth, true)
+{
+}
+
+
+POP3SStore::~POP3SStore()
+{
+}
+
+
+const string POP3SStore::getProtocolName() const
+{
+ return "pop3s";
+}
+
+
+
+// Service infos
+
+POP3ServiceInfos POP3SStore::sm_infos(true);
+
+
+const serviceInfos& POP3SStore::getInfosInstance()
+{
+ return sm_infos;
+}
+
+
+const serviceInfos& POP3SStore::getInfos() const
+{
+ return sm_infos;
+}
+
+
+} // pop3
+} // net
+} // vmime
+
diff --git a/src/net/pop3/POP3ServiceInfos.cpp b/src/net/pop3/POP3ServiceInfos.cpp
new file mode 100644
index 00000000..52387c5d
--- /dev/null
+++ b/src/net/pop3/POP3ServiceInfos.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 along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/pop3/POP3ServiceInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace pop3 {
+
+
+POP3ServiceInfos::POP3ServiceInfos(const bool pop3s)
+ : m_pop3s(pop3s)
+{
+}
+
+
+const string POP3ServiceInfos::getPropertyPrefix() const
+{
+ if (m_pop3s)
+ return "store.pop3s.";
+ else
+ return "store.pop3.";
+}
+
+
+const POP3ServiceInfos::props& POP3ServiceInfos::getProperties() const
+{
+ static props pop3Props =
+ {
+ // POP3-specific options
+ property("options.apop", serviceInfos::property::TYPE_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),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "110"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ static props pop3sProps =
+ {
+ // POP3-specific options
+ 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),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "995"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return m_pop3s ? pop3sProps : pop3Props;
+}
+
+
+const std::vector <serviceInfos::property> POP3ServiceInfos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // POP3-specific options
+ list.push_back(p.PROPERTY_OPTIONS_APOP);
+ list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK);
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (!m_pop3s)
+ {
+ list.push_back(p.PROPERTY_CONNECTION_TLS);
+ list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED);
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return list;
+}
+
+
+} // pop3
+} // net
+} // vmime
+
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp
index cabd81f0..4f373268 100644
--- a/src/net/pop3/POP3Store.cpp
+++ b/src/net/pop3/POP3Store.cpp
@@ -31,14 +31,20 @@
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/tls/TLSSession.hpp"
+#endif // VMIME_HAVE_TLS_SUPPORT
+
#include <algorithm>
// Helpers for service properties
#define GET_PROPERTY(type, prop) \
- (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+ (getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const POP3ServiceInfos&>(getInfos()).getProperties().prop))
#define HAS_PROPERTY(prop) \
- (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+ (getInfos().hasProperty(getSession(), \
+ dynamic_cast <const POP3ServiceInfos&>(getInfos()).getProperties().prop))
namespace vmime {
@@ -46,9 +52,9 @@ namespace net {
namespace pop3 {
-POP3Store::POP3Store(ref <session> sess, ref <security::authenticator> auth)
+POP3Store::POP3Store(ref <session> sess, ref <security::authenticator> auth, const bool secured)
: store(sess, getInfosInstance(), auth), m_socket(NULL),
- m_authentified(false), m_timeoutHandler(NULL)
+ m_authentified(false), m_timeoutHandler(NULL), m_secured(secured)
{
}
@@ -130,6 +136,20 @@ void POP3Store::connect()
getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
m_socket = sf->create();
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (m_secured) // dedicated port/POP3S
+ {
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ m_socket = tlsSocket;
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
m_socket->connect(address, port);
// Connection
@@ -146,6 +166,39 @@ void POP3Store::connect()
throw exceptions::connection_greeting_error(response);
}
+#if VMIME_HAVE_TLS_SUPPORT
+ // Setup secured connection, if requested
+ const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS);
+ const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED);
+
+ if (!m_secured && tls) // only if not POP3S
+ {
+ try
+ {
+ startTLS();
+ }
+ // Non-fatal error
+ catch (exceptions::command_error&)
+ {
+ if (tlsRequired)
+ {
+ throw;
+ }
+ else
+ {
+ // TLS is not required, so don't bother
+ }
+ }
+ // Fatal error
+ catch (...)
+ {
+ throw;
+ }
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
// Start authentication process
authenticate(messageId(response));
}
@@ -235,6 +288,18 @@ void POP3Store::authenticate(const messageId& randomMID)
internalDisconnect();
throw exceptions::authentication_error(response);
}
+
+ // Ensure connection is valid (cf. note above)
+ try
+ {
+ string response2;
+ sendRequest("NOOP");
+ readResponse(response2, false);
+ }
+ catch (exceptions::socket_exception&)
+ {
+ throw exceptions::authentication_error(response);
+ }
}
}
else
@@ -453,6 +518,46 @@ void POP3Store::authenticateSASL()
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+
+void POP3Store::startTLS()
+{
+ try
+ {
+ sendRequest("STLS");
+
+ string response;
+ readResponse(response, false);
+
+ if (getResponseCode(response) != RESPONSE_OK)
+ throw exceptions::command_error("STLS", response);
+
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ tlsSocket->handshake(m_timeoutHandler);
+
+ m_socket = tlsSocket;
+ }
+ catch (exceptions::command_error&)
+ {
+ // Non-fatal error
+ throw;
+ }
+ catch (exception&)
+ {
+ // Fatal error
+ internalDisconnect();
+ throw;
+ }
+}
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+
const bool POP3Store::isConnected() const
{
return (m_socket && m_socket->isConnected() && m_authentified);
@@ -478,8 +583,14 @@ void POP3Store::internalDisconnect()
m_folders.clear();
-
- sendRequest("QUIT");
+ try
+ {
+ sendRequest("QUIT");
+ }
+ catch (exception&)
+ {
+ // Not important
+ }
m_socket->disconnect();
m_socket = NULL;
@@ -627,6 +738,8 @@ void POP3Store::readResponse(string& buffer, const bool multiLine,
{
if (!m_timeoutHandler->handleTimeOut())
throw exceptions::operation_timed_out();
+
+ m_timeoutHandler->resetTimeOut();
}
// Receive data from the socket
@@ -844,78 +957,18 @@ const int POP3Store::getCapabilities() const
// Service infos
-POP3Store::_infos POP3Store::sm_infos;
+POP3ServiceInfos POP3Store::sm_infos(false);
const serviceInfos& POP3Store::getInfosInstance()
{
- return (sm_infos);
+ return sm_infos;
}
const serviceInfos& POP3Store::getInfos() const
{
- return (sm_infos);
-}
-
-
-const string POP3Store::_infos::getPropertyPrefix() const
-{
- return "store.pop3.";
-}
-
-
-const POP3Store::_infos::props& POP3Store::_infos::getProperties() const
-{
- static props p =
- {
- // POP3-specific options
- property("options.apop", serviceInfos::property::TYPE_BOOL, "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),
- property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
-
- property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
- property(serviceInfos::property::SERVER_PORT, "110"),
- property(serviceInfos::property::SERVER_SOCKETFACTORY),
-
- property(serviceInfos::property::TIMEOUT_FACTORY)
- };
-
- return p;
-}
-
-
-const std::vector <serviceInfos::property> POP3Store::_infos::getAvailableProperties() const
-{
- std::vector <property> list;
- const props& p = getProperties();
-
- // POP3-specific options
- list.push_back(p.PROPERTY_OPTIONS_APOP);
- list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK);
-#if VMIME_HAVE_SASL_SUPPORT
- list.push_back(p.PROPERTY_OPTIONS_SASL);
- list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
-#endif // VMIME_HAVE_SASL_SUPPORT
-
- // Common properties
- list.push_back(p.PROPERTY_AUTH_USERNAME);
- list.push_back(p.PROPERTY_AUTH_PASSWORD);
-
- list.push_back(p.PROPERTY_SERVER_ADDRESS);
- list.push_back(p.PROPERTY_SERVER_PORT);
- list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
-
- list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
-
- return (list);
+ return sm_infos;
}
diff --git a/src/net/sendmail/sendmailServiceInfos.cpp b/src/net/sendmail/sendmailServiceInfos.cpp
new file mode 100644
index 00000000..f0f2fa59
--- /dev/null
+++ b/src/net/sendmail/sendmailServiceInfos.cpp
@@ -0,0 +1,65 @@
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/sendmail/sendmailServiceInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace sendmail {
+
+
+sendmailServiceInfos::sendmailServiceInfos()
+{
+}
+
+
+const string sendmailServiceInfos::getPropertyPrefix() const
+{
+ return "transport.sendmail.";
+}
+
+
+const sendmailServiceInfos::props& sendmailServiceInfos::getProperties() const
+{
+ static props sendmailProps =
+ {
+ // Path to sendmail (override default)
+ property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH))
+ };
+
+ return sendmailProps;
+}
+
+
+const std::vector <serviceInfos::property> sendmailServiceInfos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ list.push_back(p.PROPERTY_BINPATH);
+
+ return list;
+}
+
+
+} // sendmail
+} // net
+} // vmime
+
diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp
index 4a077465..853f4eec 100644
--- a/src/net/sendmail/sendmailTransport.cpp
+++ b/src/net/sendmail/sendmailTransport.cpp
@@ -33,9 +33,11 @@
// Helpers for service properties
#define GET_PROPERTY(type, prop) \
- (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+ (getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const sendmailServiceInfos&>(getInfos()).getProperties().prop))
#define HAS_PROPERTY(prop) \
- (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+ (getInfos().hasProperty(getSession(), \
+ dynamic_cast <const sendmailServiceInfos&>(getInfos()).getProperties().prop))
#if VMIME_BUILTIN_PLATFORM_POSIX
@@ -177,47 +179,18 @@ void sendmailTransport::internalSend
// Service infos
-sendmailTransport::_infos sendmailTransport::sm_infos;
+sendmailServiceInfos sendmailTransport::sm_infos;
const serviceInfos& sendmailTransport::getInfosInstance()
{
- return (sm_infos);
+ return sm_infos;
}
const serviceInfos& sendmailTransport::getInfos() const
{
- return (sm_infos);
-}
-
-
-const string sendmailTransport::_infos::getPropertyPrefix() const
-{
- return "transport.sendmail.";
-}
-
-
-const sendmailTransport::_infos::props& sendmailTransport::_infos::getProperties() const
-{
- static props p =
- {
- // Path to sendmail (override default)
- property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH))
- };
-
- return p;
-}
-
-
-const std::vector <serviceInfos::property> sendmailTransport::_infos::getAvailableProperties() const
-{
- std::vector <property> list;
- const props& p = getProperties();
-
- list.push_back(p.PROPERTY_BINPATH);
-
- return (list);
+ return sm_infos;
}
diff --git a/src/net/service.cpp b/src/net/service.cpp
index acdfb390..35e8aa55 100644
--- a/src/net/service.cpp
+++ b/src/net/service.cpp
@@ -26,6 +26,10 @@
#include "vmime/security/defaultAuthenticator.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/tls/defaultCertificateVerifier.hpp"
+#endif // VMIME_HAVE_TLS_SUPPORT
+
namespace vmime {
namespace net {
@@ -45,6 +49,11 @@ service::service(ref <session> sess, const serviceInfos& /* infos */,
<security::defaultAuthenticator>();
#endif // VMIME_HAVE_SASL_SUPPORT
}
+
+#if VMIME_HAVE_TLS_SUPPORT
+ m_certVerifier = vmime::create <tls::defaultCertificateVerifier>();
+#endif // VMIME_HAVE_TLS_SUPPORT
+
}
@@ -83,5 +92,21 @@ void service::setAuthenticator(ref <security::authenticator> auth)
}
+#if VMIME_HAVE_TLS_SUPPORT
+
+void service::setCertificateVerifier(ref <tls::certificateVerifier> cv)
+{
+ m_certVerifier = cv;
+}
+
+
+ref <tls::certificateVerifier> service::getCertificateVerifier()
+{
+ return m_certVerifier;
+}
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+
} // net
} // vmime
diff --git a/src/net/serviceInfos.cpp b/src/net/serviceInfos.cpp
index 68d78c73..306c1ec3 100644
--- a/src/net/serviceInfos.cpp
+++ b/src/net/serviceInfos.cpp
@@ -46,6 +46,16 @@ const serviceInfos::property serviceInfos::property::AUTH_PASSWORD
const serviceInfos::property serviceInfos::property::TIMEOUT_FACTORY
("timeout.factory", serviceInfos::property::TYPE_STRING);
+#if VMIME_HAVE_TLS_SUPPORT
+
+const serviceInfos::property serviceInfos::property::CONNECTION_TLS
+ ("connection.tls", serviceInfos::property::TYPE_BOOL, "false");
+
+const serviceInfos::property serviceInfos::property::CONNECTION_TLS_REQUIRED
+ ("connection.tls.required", serviceInfos::property::TYPE_BOOL, "false");
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
// serviceInfos
diff --git a/src/net/smtp/SMTPSTransport.cpp b/src/net/smtp/SMTPSTransport.cpp
new file mode 100644
index 00000000..22bf4e4b
--- /dev/null
+++ b/src/net/smtp/SMTPSTransport.cpp
@@ -0,0 +1,66 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/smtp/SMTPSTransport.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPSTransport::SMTPSTransport(ref <session> sess, ref <security::authenticator> auth)
+ : SMTPTransport(sess, auth, true)
+{
+}
+
+
+SMTPSTransport::~SMTPSTransport()
+{
+}
+
+
+const string SMTPSTransport::getProtocolName() const
+{
+ return "smtps";
+}
+
+
+
+// Service infos
+
+SMTPServiceInfos SMTPSTransport::sm_infos(true);
+
+
+const serviceInfos& SMTPSTransport::getInfosInstance()
+{
+ return sm_infos;
+}
+
+
+const serviceInfos& SMTPSTransport::getInfos() const
+{
+ return sm_infos;
+}
+
+
+} // smtp
+} // net
+} // vmime
+
diff --git a/src/net/smtp/SMTPServiceInfos.cpp b/src/net/smtp/SMTPServiceInfos.cpp
new file mode 100644
index 00000000..1f0bca6c
--- /dev/null
+++ b/src/net/smtp/SMTPServiceInfos.cpp
@@ -0,0 +1,136 @@
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
+//
+
+#include "vmime/net/smtp/SMTPServiceInfos.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace smtp {
+
+
+SMTPServiceInfos::SMTPServiceInfos(const bool smtps)
+ : m_smtps(smtps)
+{
+}
+
+
+const string SMTPServiceInfos::getPropertyPrefix() const
+{
+ if (m_smtps)
+ return "transport.smtps.";
+ else
+ return "transport.smtp.";
+}
+
+
+const SMTPServiceInfos::props& SMTPServiceInfos::getProperties() const
+{
+ static props smtpProps =
+ {
+ // SMTP-specific options
+ property("options.need-authentication", serviceInfos::property::TYPE_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, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "25"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ static props smtpsProps =
+ {
+ // 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, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED),
+
+#if VMIME_HAVE_TLS_SUPPORT
+ property(serviceInfos::property::CONNECTION_TLS),
+ property(serviceInfos::property::CONNECTION_TLS_REQUIRED),
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
+ property(serviceInfos::property::SERVER_PORT, "465"),
+ property(serviceInfos::property::SERVER_SOCKETFACTORY),
+
+ property(serviceInfos::property::TIMEOUT_FACTORY)
+ };
+
+ return m_smtps ? smtpsProps : smtpProps;
+}
+
+
+const std::vector <serviceInfos::property> SMTPServiceInfos::getAvailableProperties() const
+{
+ std::vector <property> list;
+ const props& p = getProperties();
+
+ // SMTP-specific options
+ list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH);
+#if VMIME_HAVE_SASL_SUPPORT
+ list.push_back(p.PROPERTY_OPTIONS_SASL);
+ list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
+#endif // VMIME_HAVE_SASL_SUPPORT
+
+ // Common properties
+ list.push_back(p.PROPERTY_AUTH_USERNAME);
+ list.push_back(p.PROPERTY_AUTH_PASSWORD);
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (!m_smtps)
+ {
+ list.push_back(p.PROPERTY_CONNECTION_TLS);
+ list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED);
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+ list.push_back(p.PROPERTY_SERVER_ADDRESS);
+ list.push_back(p.PROPERTY_SERVER_PORT);
+ list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
+
+ list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
+
+ return list;
+}
+
+
+} // smtp
+} // net
+} // vmime
+
diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp
index 1aa15c13..d0c72a13 100644
--- a/src/net/smtp/SMTPTransport.cpp
+++ b/src/net/smtp/SMTPTransport.cpp
@@ -24,8 +24,6 @@
#include "vmime/encoderB64.hpp"
#include "vmime/mailboxList.hpp"
-#include "vmime/net/authHelper.hpp"
-
#include "vmime/utility/filteredStream.hpp"
#include "vmime/utility/stringUtils.hpp"
@@ -33,12 +31,18 @@
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+ #include "vmime/net/tls/TLSSession.hpp"
+#endif // VMIME_HAVE_TLS_SUPPORT
+
// Helpers for service properties
#define GET_PROPERTY(type, prop) \
- (sm_infos.getPropertyValue <type>(getSession(), sm_infos.getProperties().prop))
+ (getInfos().getPropertyValue <type>(getSession(), \
+ dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().prop))
#define HAS_PROPERTY(prop) \
- (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop))
+ (getInfos().hasProperty(getSession(), \
+ dynamic_cast <const SMTPServiceInfos&>(getInfos()).getProperties().prop))
namespace vmime {
@@ -46,9 +50,10 @@ namespace net {
namespace smtp {
-SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth)
+SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
- m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL)
+ m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL),
+ m_secured(secured)
{
}
@@ -97,6 +102,20 @@ void SMTPTransport::connect()
getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY));
m_socket = sf->create();
+
+#if VMIME_HAVE_TLS_SUPPORT
+ if (m_secured) // dedicated port/SMTPS
+ {
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ m_socket = tlsSocket;
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
m_socket->connect(address, port);
m_responseBuffer.clear();
@@ -146,6 +165,39 @@ void SMTPTransport::connect()
m_extendedSMTPResponse = response;
}
+#if VMIME_HAVE_TLS_SUPPORT
+ // Setup secured connection, if requested
+ const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS);
+ const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED)
+ && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED);
+
+ if (!m_secured && tls) // only if not POP3S
+ {
+ try
+ {
+ startTLS();
+ }
+ // Non-fatal error
+ catch (exceptions::command_error&)
+ {
+ if (tlsRequired)
+ {
+ throw;
+ }
+ else
+ {
+ // TLS is not required, so don't bother
+ }
+ }
+ // Fatal error
+ catch (...)
+ {
+ throw;
+ }
+ }
+#endif // VMIME_HAVE_TLS_SUPPORT
+
// Authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH))
authenticate();
@@ -369,6 +421,45 @@ void SMTPTransport::authenticateSASL()
#endif // VMIME_HAVE_SASL_SUPPORT
+#if VMIME_HAVE_TLS_SUPPORT
+
+void SMTPTransport::startTLS()
+{
+ try
+ {
+ sendRequest("STARTTLS");
+
+ string response;
+
+ if (readAllResponses(response) != 220)
+ throw exceptions::command_error("STARTTLS", response);
+
+ ref <tls::TLSSession> tlsSession =
+ vmime::create <tls::TLSSession>(getCertificateVerifier());
+
+ ref <tls::TLSSocket> tlsSocket =
+ tlsSession->getSocket(m_socket);
+
+ tlsSocket->handshake(m_timeoutHandler);
+
+ m_socket = tlsSocket;
+ }
+ catch (exceptions::command_error&)
+ {
+ // Non-fatal error
+ throw;
+ }
+ catch (exception&)
+ {
+ // Fatal error
+ internalDisconnect();
+ throw;
+ }
+}
+
+#endif // VMIME_HAVE_TLS_SUPPORT
+
+
const bool SMTPTransport::isConnected() const
{
return (m_socket && m_socket->isConnected() && m_authentified);
@@ -516,6 +607,8 @@ const string SMTPTransport::readResponseLine()
{
if (!m_timeoutHandler->handleTimeOut())
throw exceptions::operation_timed_out();
+
+ m_timeoutHandler->resetTimeOut();
}
// Receive data from the socket
@@ -589,76 +682,18 @@ const int SMTPTransport::readAllResponses(string& outText, const bool allText)
// Service infos
-SMTPTransport::_infos SMTPTransport::sm_infos;
+SMTPServiceInfos SMTPTransport::sm_infos(false);
const serviceInfos& SMTPTransport::getInfosInstance()
{
- return (sm_infos);
+ return sm_infos;
}
const serviceInfos& SMTPTransport::getInfos() const
{
- return (sm_infos);
-}
-
-
-const string SMTPTransport::_infos::getPropertyPrefix() const
-{
- return "transport.smtp.";
-}
-
-
-const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const
-{
- static props p =
- {
- // SMTP-specific options
- property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"),
-#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),
- property(serviceInfos::property::AUTH_PASSWORD),
-
- property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED),
- property(serviceInfos::property::SERVER_PORT, "25"),
- property(serviceInfos::property::SERVER_SOCKETFACTORY),
-
- property(serviceInfos::property::TIMEOUT_FACTORY)
- };
-
- return p;
-}
-
-
-const std::vector <serviceInfos::property> SMTPTransport::_infos::getAvailableProperties() const
-{
- std::vector <property> list;
- const props& p = getProperties();
-
- // SMTP-specific options
- list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH);
-#if VMIME_HAVE_SASL_SUPPORT
- list.push_back(p.PROPERTY_OPTIONS_SASL);
- list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
-#endif // VMIME_HAVE_SASL_SUPPORT
-
- // Common properties
- list.push_back(p.PROPERTY_AUTH_USERNAME);
- list.push_back(p.PROPERTY_AUTH_PASSWORD);
-
- list.push_back(p.PROPERTY_SERVER_ADDRESS);
- list.push_back(p.PROPERTY_SERVER_PORT);
- list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY);
-
- list.push_back(p.PROPERTY_TIMEOUT_FACTORY);
-
- return (list);
+ return sm_infos;
}
diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp
new file mode 100644
index 00000000..fb84714c
--- /dev/null
+++ b/src/net/tls/TLSSession.cpp
@@ -0,0 +1,342 @@
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include <gnutls/gnutls.h>
+#include <gnutls/extra.h>
+
+#include "vmime/net/tls/TLSSession.hpp"
+
+#include "vmime/exception.hpp"
+
+
+// Enable GnuTLS debugging by defining GNUTLS_DEBUG
+//#define GNUTLS_DEBUG 1
+
+
+#if VMIME_DEBUG && GNUTLS_DEBUG
+ #include <iostream>
+#endif // VMIME_DEBUG && GNUTLS_DEBUG
+
+
+namespace vmime {
+namespace net {
+namespace tls {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+// Initialize GNU TLS library
+struct TLSGlobal
+{
+ TLSGlobal()
+ {
+ gnutls_global_init();
+ //gnutls_global_init_extra();
+
+#if VMIME_DEBUG && GNUTLS_DEBUG
+ gnutls_global_set_log_function(TLSLogFunc);
+ gnutls_global_set_log_level(10);
+#endif // VMIME_DEBUG && GNUTLS_DEBUG
+
+ gnutls_anon_allocate_client_credentials(&anonCred);
+ gnutls_certificate_allocate_credentials(&certCred);
+ }
+
+ ~TLSGlobal()
+ {
+ gnutls_anon_free_client_credentials(anonCred);
+ gnutls_certificate_free_credentials(certCred);
+
+ gnutls_global_deinit();
+ }
+
+#if VMIME_DEBUG && GNUTLS_DEBUG
+
+ static void TLSLogFunc(int level, const char *str)
+ {
+ std::cerr << "GNUTLS: [" << level << "] " << str << std::endl;
+ }
+
+#endif // VMIME_DEBUG && GNUTLS_DEBUG
+
+
+ gnutls_anon_client_credentials anonCred;
+ gnutls_certificate_credentials certCred;
+};
+
+static TLSGlobal g_gnutlsGlobal;
+
+
+#endif // VMIME_BUILDING_DOC
+
+
+
+TLSSession::TLSSession(ref <certificateVerifier> cv)
+ : m_certVerifier(cv)
+{
+ int res;
+
+ m_gnutlsSession = new gnutls_session;
+
+ if (gnutls_init(m_gnutlsSession, GNUTLS_CLIENT) != 0)
+ throw std::bad_alloc();
+
+ // Sets some default priority on the ciphers, key exchange methods,
+ // macs and compression methods.
+ gnutls_set_default_priority(*m_gnutlsSession);
+
+ // Sets the priority on the certificate types supported by gnutls.
+ // Priority is higher for types specified before others. After
+ // specifying the types you want, you must append a 0.
+ const int certTypePriority[] = { GNUTLS_CRT_X509, 0 };
+
+ res = gnutls_certificate_type_set_priority
+ (*m_gnutlsSession, certTypePriority);
+
+ if (res < 0)
+ {
+ throwTLSException
+ ("gnutls_certificate_type_set_priority", res);
+ }
+
+ // Sets the priority on the protocol types
+ const int protoPriority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
+
+ res = gnutls_protocol_set_priority(*m_gnutlsSession, protoPriority);
+
+ if (res < 0)
+ {
+ throwTLSException
+ ("gnutls_certificate_type_set_priority", res);
+ }
+
+ // Priority on the ciphers
+ const int cipherPriority[] =
+ {
+ GNUTLS_CIPHER_ARCFOUR_128,
+ GNUTLS_CIPHER_3DES_CBC,
+ GNUTLS_CIPHER_AES_128_CBC,
+ GNUTLS_CIPHER_AES_256_CBC,
+ GNUTLS_CIPHER_ARCFOUR_40,
+ GNUTLS_CIPHER_RC2_40_CBC,
+ GNUTLS_CIPHER_DES_CBC,
+ 0
+ };
+
+ gnutls_cipher_set_priority(*m_gnutlsSession, cipherPriority);
+
+ // Priority on MACs
+ const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0};
+
+ gnutls_mac_set_priority(*m_gnutlsSession, macPriority);
+
+ // Priority on key exchange methods
+ const int kxPriority[] =
+ {
+ GNUTLS_KX_RSA,
+ GNUTLS_KX_DHE_DSS,
+ GNUTLS_KX_DHE_RSA,
+ GNUTLS_KX_ANON_DH,
+ GNUTLS_KX_SRP,
+ GNUTLS_KX_RSA_EXPORT,
+ GNUTLS_KX_SRP_RSA,
+ GNUTLS_KX_SRP_DSS,
+ 0
+ };
+
+ gnutls_kx_set_priority(*m_gnutlsSession, kxPriority);
+
+ // Priority on compression methods
+ const int compressionPriority[] =
+ {
+ GNUTLS_COMP_ZLIB,
+ //GNUTLS_COMP_LZO,
+ GNUTLS_COMP_NULL,
+ 0
+ };
+
+ gnutls_compression_set_priority(*m_gnutlsSession, compressionPriority);
+
+ // Initialize credentials
+ gnutls_credentials_set(*m_gnutlsSession,
+ GNUTLS_CRD_ANON, &g_gnutlsGlobal.anonCred);
+
+ gnutls_credentials_set(*m_gnutlsSession,
+ GNUTLS_CRD_CERTIFICATE, &g_gnutlsGlobal.certCred);
+}
+
+
+TLSSession::TLSSession(const TLSSession&)
+ : object()
+{
+ // Not used
+}
+
+
+TLSSession::~TLSSession()
+{
+ if (m_gnutlsSession)
+ {
+ gnutls_deinit(*m_gnutlsSession);
+
+ delete m_gnutlsSession;
+ m_gnutlsSession = NULL;
+ }
+}
+
+
+ref <TLSSocket> TLSSession::getSocket(ref <socket> sok)
+{
+ return vmime::create <TLSSocket>
+ (thisRef().dynamicCast <TLSSession>(), sok);
+}
+
+
+ref <tls::certificateVerifier> TLSSession::getCertificateVerifier()
+{
+ return m_certVerifier;
+}
+
+
+void TLSSession::throwTLSException(const string& fname, const int code)
+{
+ string msg = fname + "() returned ";
+
+#define ERROR(x) \
+ case x: msg += #x; break;
+
+ switch (code)
+ {
+ ERROR(GNUTLS_E_SUCCESS)
+ ERROR(GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM)
+ ERROR(GNUTLS_E_UNKNOWN_CIPHER_TYPE)
+ ERROR(GNUTLS_E_LARGE_PACKET)
+ ERROR(GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+ ERROR(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+ ERROR(GNUTLS_E_INVALID_SESSION)
+ ERROR(GNUTLS_E_FATAL_ALERT_RECEIVED)
+ ERROR(GNUTLS_E_UNEXPECTED_PACKET)
+ ERROR(GNUTLS_E_WARNING_ALERT_RECEIVED)
+ ERROR(GNUTLS_E_ERROR_IN_FINISHED_PACKET)
+ ERROR(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
+ ERROR(GNUTLS_E_UNKNOWN_CIPHER_SUITE)
+ ERROR(GNUTLS_E_UNWANTED_ALGORITHM)
+ ERROR(GNUTLS_E_MPI_SCAN_FAILED)
+ ERROR(GNUTLS_E_DECRYPTION_FAILED)
+ ERROR(GNUTLS_E_MEMORY_ERROR)
+ ERROR(GNUTLS_E_DECOMPRESSION_FAILED)
+ ERROR(GNUTLS_E_COMPRESSION_FAILED)
+ ERROR(GNUTLS_E_AGAIN)
+ ERROR(GNUTLS_E_EXPIRED)
+ ERROR(GNUTLS_E_DB_ERROR)
+ ERROR(GNUTLS_E_SRP_PWD_ERROR)
+ ERROR(GNUTLS_E_INSUFFICIENT_CREDENTIALS)
+ ERROR(GNUTLS_E_HASH_FAILED)
+ ERROR(GNUTLS_E_BASE64_DECODING_ERROR)
+ ERROR(GNUTLS_E_MPI_PRINT_FAILED)
+ ERROR(GNUTLS_E_REHANDSHAKE)
+ ERROR(GNUTLS_E_GOT_APPLICATION_DATA)
+ ERROR(GNUTLS_E_RECORD_LIMIT_REACHED)
+ ERROR(GNUTLS_E_ENCRYPTION_FAILED)
+ ERROR(GNUTLS_E_PK_ENCRYPTION_FAILED)
+ ERROR(GNUTLS_E_PK_DECRYPTION_FAILED)
+ ERROR(GNUTLS_E_PK_SIGN_FAILED)
+ ERROR(GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION)
+ ERROR(GNUTLS_E_KEY_USAGE_VIOLATION)
+ ERROR(GNUTLS_E_NO_CERTIFICATE_FOUND)
+ ERROR(GNUTLS_E_INVALID_REQUEST)
+ ERROR(GNUTLS_E_SHORT_MEMORY_BUFFER)
+ ERROR(GNUTLS_E_INTERRUPTED)
+ ERROR(GNUTLS_E_PUSH_ERROR)
+ ERROR(GNUTLS_E_PULL_ERROR)
+ ERROR(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER)
+ ERROR(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ ERROR(GNUTLS_E_PKCS1_WRONG_PAD)
+ ERROR(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION)
+ ERROR(GNUTLS_E_INTERNAL_ERROR)
+ ERROR(GNUTLS_E_DH_PRIME_UNACCEPTABLE)
+ ERROR(GNUTLS_E_FILE_ERROR)
+ ERROR(GNUTLS_E_TOO_MANY_EMPTY_PACKETS)
+ ERROR(GNUTLS_E_UNKNOWN_PK_ALGORITHM)
+ ERROR(GNUTLS_E_INIT_LIBEXTRA)
+ ERROR(GNUTLS_E_LIBRARY_VERSION_MISMATCH)
+ ERROR(GNUTLS_E_NO_TEMPORARY_RSA_PARAMS)
+ ERROR(GNUTLS_E_LZO_INIT_FAILED)
+ ERROR(GNUTLS_E_NO_COMPRESSION_ALGORITHMS)
+ ERROR(GNUTLS_E_NO_CIPHER_SUITES)
+ ERROR(GNUTLS_E_OPENPGP_GETKEY_FAILED)
+ ERROR(GNUTLS_E_PK_SIG_VERIFY_FAILED)
+ ERROR(GNUTLS_E_ILLEGAL_SRP_USERNAME)
+ ERROR(GNUTLS_E_SRP_PWD_PARSING_ERROR)
+ ERROR(GNUTLS_E_NO_TEMPORARY_DH_PARAMS)
+ ERROR(GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ ERROR(GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND)
+ ERROR(GNUTLS_E_ASN1_DER_ERROR)
+ ERROR(GNUTLS_E_ASN1_VALUE_NOT_FOUND)
+ ERROR(GNUTLS_E_ASN1_GENERIC_ERROR)
+ ERROR(GNUTLS_E_ASN1_VALUE_NOT_VALID)
+ ERROR(GNUTLS_E_ASN1_TAG_ERROR)
+ ERROR(GNUTLS_E_ASN1_TAG_IMPLICIT)
+ ERROR(GNUTLS_E_ASN1_TYPE_ANY_ERROR)
+ ERROR(GNUTLS_E_ASN1_SYNTAX_ERROR)
+ ERROR(GNUTLS_E_ASN1_DER_OVERFLOW)
+ ERROR(GNUTLS_E_OPENPGP_TRUSTDB_VERSION_UNSUPPORTED)
+ ERROR(GNUTLS_E_OPENPGP_UID_REVOKED)
+ ERROR(GNUTLS_E_CERTIFICATE_ERROR)
+ //ERROR(GNUTLS_E_X509_CERTIFICATE_ERROR)
+ ERROR(GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+ ERROR(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE)
+ ERROR(GNUTLS_E_X509_UNKNOWN_SAN)
+ ERROR(GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED)
+ ERROR(GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE)
+ ERROR(GNUTLS_E_UNKNOWN_HASH_ALGORITHM)
+ ERROR(GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE)
+ ERROR(GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE)
+ ERROR(GNUTLS_E_INVALID_PASSWORD)
+ ERROR(GNUTLS_E_MAC_VERIFY_FAILED)
+ ERROR(GNUTLS_E_CONSTRAINT_ERROR)
+ ERROR(GNUTLS_E_BASE64_ENCODING_ERROR)
+ ERROR(GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY)
+ //ERROR(GNUTLS_E_INCOMPATIBLE_CRYPTO_LIBRARY)
+ ERROR(GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY)
+ ERROR(GNUTLS_E_OPENPGP_KEYRING_ERROR)
+ ERROR(GNUTLS_E_X509_UNSUPPORTED_OID)
+ //ERROR(GNUTLS_E_RANDOM_FAILED)
+ ERROR(GNUTLS_E_UNIMPLEMENTED_FEATURE)
+
+ default:
+
+ msg += "unknown error";
+ break;
+ }
+
+#undef ERROR
+
+ throw exceptions::tls_exception(msg);
+}
+
+
+} // tls
+} // net
+} // vmime
+
diff --git a/src/net/tls/TLSSocket.cpp b/src/net/tls/TLSSocket.cpp
new file mode 100644
index 00000000..ebf3214b
--- /dev/null
+++ b/src/net/tls/TLSSocket.cpp
@@ -0,0 +1,391 @@
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "vmime/net/tls/TLSSocket.hpp"
+#include "vmime/net/tls/TLSSession.hpp"
+
+#include "vmime/platformDependant.hpp"
+
+#include "vmime/net/tls/X509Certificate.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace tls {
+
+
+TLSSocket::TLSSocket(ref <TLSSession> session, ref <socket> sok)
+ : m_session(session), m_wrapped(sok), m_connected(false),
+ m_handshaking(false), m_ex(NULL)
+{
+ gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this);
+
+ gnutls_transport_set_push_function(*m_session->m_gnutlsSession, gnutlsPushFunc);
+ gnutls_transport_set_pull_function(*m_session->m_gnutlsSession, gnutlsPullFunc);
+}
+
+
+TLSSocket::~TLSSocket()
+{
+ try
+ {
+ disconnect();
+ }
+ catch (...)
+ {
+ // Don't throw exception in destructor
+ }
+}
+
+
+void TLSSocket::connect(const string& address, const port_t port)
+{
+ m_wrapped->connect(address, port);
+
+ handshake(NULL);
+
+ m_connected = true;
+}
+
+
+void TLSSocket::disconnect()
+{
+ if (m_connected)
+ {
+ gnutls_bye(*m_session->m_gnutlsSession, GNUTLS_SHUT_RDWR);
+
+ m_wrapped->disconnect();
+
+ m_connected = false;
+ }
+}
+
+
+const bool TLSSocket::isConnected() const
+{
+ return m_wrapped->isConnected() && m_connected;
+}
+
+
+void TLSSocket::receive(string& buffer)
+{
+ const int size = receiveRaw(m_buffer, sizeof(m_buffer));
+ buffer = vmime::string(m_buffer, size);
+}
+
+
+void TLSSocket::send(const string& buffer)
+{
+ sendRaw(buffer.data(), buffer.length());
+}
+
+
+const int TLSSocket::receiveRaw(char* buffer, const int count)
+{
+ const ssize_t ret = gnutls_record_recv
+ (*m_session->m_gnutlsSession,
+ buffer, static_cast <size_t>(count));
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN)
+ return 0;
+
+ TLSSession::throwTLSException("gnutls_record_recv", ret);
+ }
+
+ return static_cast <int>(ret);
+}
+
+
+void TLSSocket::sendRaw(const char* buffer, const int count)
+{
+ gnutls_record_send
+ (*m_session->m_gnutlsSession,
+ buffer, static_cast <size_t>(count));
+
+ if (m_ex)
+ internalThrow();
+}
+
+
+void TLSSocket::handshake(ref <timeoutHandler> toHandler)
+{
+ if (toHandler)
+ toHandler->resetTimeOut();
+
+ // Start handshaking process
+ m_handshaking = true;
+ m_toHandler = toHandler;
+
+ try
+ {
+ while (true)
+ {
+ const int ret = gnutls_handshake(*m_session->m_gnutlsSession);
+
+ if (m_ex)
+ internalThrow();
+
+ if (ret < 0)
+ {
+ if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED)
+ {
+ // Non-fatal error
+ platformDependant::getHandler()->wait();
+ }
+ else
+ {
+ TLSSession::throwTLSException("gnutls_handshake", ret);
+ }
+ }
+ else
+ {
+ // Successful handshake
+ break;
+ }
+ }
+ }
+ catch (...)
+ {
+ m_handshaking = false;
+ m_toHandler = NULL;
+
+ throw;
+ }
+
+ m_handshaking = false;
+ m_toHandler = NULL;
+
+ // Verify server's certificate(s)
+ ref <certificateChain> certs = getPeerCertificates();
+
+ if (certs == NULL)
+ throw exceptions::tls_exception("No peer certificate.");
+
+ m_session->getCertificateVerifier()->verify(certs);
+
+ m_connected = true;
+}
+
+
+ssize_t TLSSocket::gnutlsPushFunc
+ (gnutls_transport_ptr trspt, const void* data, size_t len)
+{
+ TLSSocket* sok = reinterpret_cast <TLSSocket*>(trspt);
+
+ try
+ {
+ sok->m_wrapped->sendRaw
+ (reinterpret_cast <const char*>(data), static_cast <int>(len));
+ }
+ catch (exception& e)
+ {
+ // Workaround for bad behaviour when throwing C++ exceptions
+ // from C functions (GNU TLS)
+ sok->m_ex = e.clone();
+ return -1;
+ }
+
+ return len;
+}
+
+
+ssize_t TLSSocket::gnutlsPullFunc
+ (gnutls_transport_ptr trspt, void* data, size_t len)
+{
+ TLSSocket* sok = reinterpret_cast <TLSSocket*>(trspt);
+
+ try
+ {
+ // Workaround for cross-platform asynchronous handshaking:
+ // gnutls_handshake() only returns GNUTLS_E_AGAIN if recv()
+ // returns -1 and errno is set to EGAIN...
+ if (sok->m_handshaking)
+ {
+ while (true)
+ {
+ const ssize_t ret = static_cast <ssize_t>
+ (sok->m_wrapped->receiveRaw
+ (reinterpret_cast <char*>(data),
+ static_cast <int>(len)));
+
+ if (ret == 0)
+ {
+ // No data available yet
+ platformDependant::getHandler()->wait();
+ }
+ else
+ {
+ return ret;
+ }
+
+ // Check whether the time-out delay is elapsed
+ if (sok->m_toHandler && sok->m_toHandler->isTimeOut())
+ {
+ if (!sok->m_toHandler->handleTimeOut())
+ throw exceptions::operation_timed_out();
+
+ sok->m_toHandler->resetTimeOut();
+ }
+ }
+ }
+ else
+ {
+ const ssize_t n = static_cast <ssize_t>
+ (sok->m_wrapped->receiveRaw
+ (reinterpret_cast <char*>(data),
+ static_cast <int>(len)));
+
+ if (n == 0)
+ return GNUTLS_E_AGAIN; // This seems like a hack, really...
+
+ return n;
+ }
+ }
+ catch (exception& e)
+ {
+ // Workaround for bad behaviour when throwing C++ exceptions
+ // from C functions (GNU TLS)
+ sok->m_ex = e.clone();
+ return -1;
+ }
+}
+
+
+ref <certificateChain> TLSSocket::getPeerCertificates()
+{
+ unsigned int certCount = 0;
+ const gnutls_datum* rawData = gnutls_certificate_get_peers
+ (*m_session->m_gnutlsSession, &certCount);
+
+ // Try X.509
+ gnutls_x509_crt* x509Certs = new gnutls_x509_crt[certCount];
+
+ unsigned int count = certCount;
+
+ int res = gnutls_x509_crt_list_import
+ (x509Certs, &count, rawData, GNUTLS_X509_FMT_PEM, 0);
+
+ if (res <= 0)
+ {
+ count = certCount;
+
+ res = gnutls_x509_crt_list_import
+ (x509Certs, &count, rawData, GNUTLS_X509_FMT_DER, 0);
+ }
+
+ if (res >= 1)
+ {
+ std::vector <ref <certificate> > certs;
+ bool error = false;
+
+ count = static_cast <unsigned int>(res);
+
+ for (unsigned int i = 0 ; i < count ; ++i)
+ {
+ size_t dataSize = 0;
+
+ gnutls_x509_crt_export(x509Certs[i],
+ GNUTLS_X509_FMT_DER, NULL, &dataSize);
+
+ byte* data = new byte[dataSize];
+
+ gnutls_x509_crt_export(x509Certs[i],
+ GNUTLS_X509_FMT_DER, data, &dataSize);
+
+ ref <X509Certificate> cert =
+ X509Certificate::import(data, dataSize);
+
+ if (cert != NULL)
+ certs.push_back(cert);
+ else
+ error = true;
+
+ delete [] data;
+
+ gnutls_x509_crt_deinit(x509Certs[i]);
+ }
+
+ delete [] x509Certs;
+
+ if (error)
+ return NULL;
+
+ return vmime::create <certificateChain>(certs);
+ }
+
+ delete [] x509Certs;
+
+ return NULL;
+}
+
+
+// Following is a workaround for C++ exceptions to pass correctly between
+// C and C++ calls.
+//
+// gnutls_record_recv() calls TLSSocket::gnutlsPullFunc, and exceptions
+// thrown by the socket can not not catched.
+
+#ifndef VMIME_BUILDING_DOC
+
+class TLSSocket_DeleteExWrapper : public object
+{
+public:
+
+ TLSSocket_DeleteExWrapper(exception* ex) : m_ex(ex) { }
+ ~TLSSocket_DeleteExWrapper() { delete m_ex; }
+
+private:
+
+ exception* m_ex;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+void TLSSocket::internalThrow()
+{
+ static std::vector <ref <TLSSocket_DeleteExWrapper> > exToDelete;
+
+ if (m_ex)
+ {
+ // To avoid memory leaks
+ exToDelete.push_back(vmime::create <TLSSocket_DeleteExWrapper>(m_ex));
+
+ throw *m_ex;
+ }
+}
+
+
+} // tls
+} // net
+} // vmime
+
diff --git a/src/net/tls/X509Certificate.cpp b/src/net/tls/X509Certificate.cpp
new file mode 100644
index 00000000..cfb52a1d
--- /dev/null
+++ b/src/net/tls/X509Certificate.cpp
@@ -0,0 +1,273 @@
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include <ctime>
+
+#include "vmime/net/tls/X509Certificate.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace tls {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+struct X509CertificateInternalData
+{
+ X509CertificateInternalData()
+ {
+ gnutls_x509_crt_init(&cert);
+ }
+
+ ~X509CertificateInternalData()
+ {
+ gnutls_x509_crt_deinit(cert);
+ }
+
+
+ gnutls_x509_crt cert;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+X509Certificate::X509Certificate()
+ : m_data(new X509CertificateInternalData)
+{
+}
+
+
+X509Certificate::X509Certificate(const X509Certificate&)
+ : certificate(), m_data(NULL)
+{
+ // Not used
+}
+
+
+X509Certificate::~X509Certificate()
+{
+ delete m_data;
+}
+
+
+// static
+ref <X509Certificate> X509Certificate::import(utility::inputStream& is)
+{
+ byteArray bytes;
+ utility::stream::value_type chunk[4096];
+
+ while (!is.eof())
+ {
+ const int len = is.read(chunk, sizeof(chunk));
+ bytes.insert(bytes.end(), chunk, chunk + len);
+ }
+
+ return import(&bytes[0], bytes.size());
+}
+
+
+// static
+ref <X509Certificate> X509Certificate::import
+ (const byte* data, const unsigned int length)
+{
+ ref <X509Certificate> cert = vmime::create <X509Certificate>();
+
+ gnutls_datum buffer;
+ buffer.data = const_cast <byte*>(data);
+ buffer.size = length;
+
+ // Try DER format
+ if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_DER) >= 0)
+ return cert;
+
+ // Try PEM format
+ if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_PEM) >= 0)
+ return cert;
+
+ return NULL;
+}
+
+
+void X509Certificate::write
+ (utility::outputStream& os, const Format format) const
+{
+ size_t dataSize = 0;
+ gnutls_x509_crt_fmt fmt = GNUTLS_X509_FMT_DER;
+
+ switch (format)
+ {
+ case FORMAT_DER: fmt = GNUTLS_X509_FMT_DER; break;
+ case FORMAT_PEM: fmt = GNUTLS_X509_FMT_PEM; break;
+ }
+
+ gnutls_x509_crt_export(m_data->cert, fmt, NULL, &dataSize);
+
+ byte* data = new byte[dataSize];
+
+ gnutls_x509_crt_export(m_data->cert, fmt, data, &dataSize);
+
+ try
+ {
+ os.write(reinterpret_cast <utility::stream::value_type*>(data), dataSize);
+ }
+ catch (...)
+ {
+ delete [] data;
+ throw;
+ }
+}
+
+
+const byteArray X509Certificate::getSerialNumber() const
+{
+ char serial[64];
+ size_t serialSize = sizeof(serial);
+
+ gnutls_x509_crt_get_serial(m_data->cert, serial, &serialSize);
+
+ return byteArray(serial, serial + serialSize);
+}
+
+
+const bool X509Certificate::checkIssuer
+ (ref <const X509Certificate> issuer) const
+{
+ return (gnutls_x509_crt_check_issuer
+ (m_data->cert, issuer->m_data->cert) >= 1);
+}
+
+
+const bool X509Certificate::verify(ref <const X509Certificate> caCert) const
+{
+ unsigned int verify = 0;
+
+ const int res = gnutls_x509_crt_verify
+ (m_data->cert, &(caCert->m_data->cert), 1,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &verify);
+
+ return (res == 0 && verify == 0);
+}
+
+
+const datetime X509Certificate::getActivationDate() const
+{
+ const time_t t = gnutls_x509_crt_get_activation_time(m_data->cert);
+ return datetime(t);
+}
+
+
+const datetime X509Certificate::getExpirationDate() const
+{
+ const time_t t = gnutls_x509_crt_get_expiration_time(m_data->cert);
+ return datetime(t);
+}
+
+
+const byteArray X509Certificate::getFingerprint(const DigestAlgorithm algo) const
+{
+ gnutls_digest_algorithm galgo;
+
+ switch (algo)
+ {
+ case DIGEST_MD5:
+
+ galgo = GNUTLS_DIG_MD5;
+ break;
+
+ default:
+ case DIGEST_SHA1:
+
+ galgo = GNUTLS_DIG_SHA;
+ break;
+ }
+
+ size_t bufferSize = 0;
+ gnutls_x509_crt_get_fingerprint
+ (m_data->cert, galgo, NULL, &bufferSize);
+
+ byte* buffer = new byte[bufferSize];
+
+ if (gnutls_x509_crt_get_fingerprint
+ (m_data->cert, galgo, buffer, &bufferSize) == 0)
+ {
+ byteArray res;
+ res.insert(res.end(), buffer, buffer + bufferSize);
+
+ delete [] buffer;
+
+ return res;
+ }
+
+ delete [] buffer;
+
+ return byteArray();
+}
+
+
+const byteArray X509Certificate::getEncoded() const
+{
+ byteArray bytes;
+ utility::outputStreamByteArrayAdapter os(bytes);
+
+ write(os, FORMAT_DER);
+
+ return bytes;
+}
+
+
+const string X509Certificate::getType() const
+{
+ return "X.509";
+}
+
+
+const int X509Certificate::getVersion() const
+{
+ return gnutls_x509_crt_get_version(m_data->cert);
+}
+
+
+const bool X509Certificate::equals(ref <const certificate> other) const
+{
+ ref <const X509Certificate> otherX509 =
+ other.dynamicCast <const X509Certificate>();
+
+ if (!otherX509)
+ return false;
+
+ const byteArray fp1 = getFingerprint(DIGEST_MD5);
+ const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5);
+
+ return fp1 == fp2;
+}
+
+
+} // tls
+} // net
+} // vmime
+
diff --git a/vmime/net/authHelper.hpp b/src/net/tls/certificateChain.cpp
index 54487fbe..52855cc2 100644
--- a/vmime/net/authHelper.hpp
+++ b/src/net/tls/certificateChain.cpp
@@ -21,22 +21,33 @@
// the GNU General Public License cover the whole combination.
//
-#ifndef VMIME_NET_AUTHHELPER_HPP_INCLUDED
-#define VMIME_NET_AUTHHELPER_HPP_INCLUDED
-
-
-#include "vmime/types.hpp"
+#include "vmime/net/tls/certificateChain.hpp"
namespace vmime {
namespace net {
+namespace tls {
+
+
+certificateChain::certificateChain(const std::vector <ref <certificate> >& certs)
+ : m_certs(certs)
+{
+}
-void hmac_md5(const string& text, const string& key, string& hexDigest);
+const unsigned int certificateChain::getCount() const
+{
+ return static_cast <unsigned int>(m_certs.size());
+}
+ref <certificate> certificateChain::getAt(const unsigned int index)
+{
+ return m_certs[index];
+}
+
+
+} // tls
} // net
} // vmime
-
-#endif // VMIME_NET_AUTHHELPER_HPP_INCLUDED
diff --git a/src/net/tls/defaultCertificateVerifier.cpp b/src/net/tls/defaultCertificateVerifier.cpp
new file mode 100644
index 00000000..de0c6e45
--- /dev/null
+++ b/src/net/tls/defaultCertificateVerifier.cpp
@@ -0,0 +1,164 @@
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "vmime/net/tls/defaultCertificateVerifier.hpp"
+
+#include "vmime/net/tls/X509Certificate.hpp"
+
+#include "vmime/exception.hpp"
+
+
+namespace vmime {
+namespace net {
+namespace tls {
+
+
+defaultCertificateVerifier::defaultCertificateVerifier()
+{
+}
+
+
+defaultCertificateVerifier::~defaultCertificateVerifier()
+{
+}
+
+
+defaultCertificateVerifier::defaultCertificateVerifier(const defaultCertificateVerifier&)
+ : certificateVerifier()
+{
+ // Not used
+}
+
+
+void defaultCertificateVerifier::verify(ref <certificateChain> chain)
+{
+ if (chain->getCount() == 0)
+ return;
+
+ const string type = chain->getAt(0)->getType();
+
+ if (type == "X.509")
+ verifyX509(chain);
+ else
+ throw exceptions::unsupported_certificate_type(type);
+}
+
+
+void defaultCertificateVerifier::verifyX509(ref <certificateChain> chain)
+{
+ // For every certificate in the chain, verify that the certificate
+ // has been issued by the next certificate in the chain
+ if (chain->getCount() >= 2)
+ {
+ for (unsigned int i = 0 ; i < chain->getCount() - 1 ; ++i)
+ {
+ ref <X509Certificate> cert =
+ chain->getAt(i).dynamicCast <X509Certificate>();
+
+ ref <X509Certificate> next =
+ chain->getAt(i + 1).dynamicCast <X509Certificate>();
+
+ if (!cert->checkIssuer(next))
+ {
+ throw exceptions::certificate_verification_exception
+ ("Subject/issuer verification failed.");
+ }
+ }
+ }
+
+ // For every certificate in the chain, verify that the certificate
+ // is valid at the current time
+ const datetime now = datetime::now();
+
+ for (unsigned int i = 0 ; i < chain->getCount() ; ++i)
+ {
+ ref <X509Certificate> cert =
+ chain->getAt(i).dynamicCast <X509Certificate>();
+
+ const datetime begin = cert->getActivationDate();
+ const datetime end = cert->getExpirationDate();
+
+ if (now < begin || now > end)
+ {
+ throw exceptions::certificate_verification_exception
+ ("Validity date check failed.");
+ }
+ }
+
+ // Check whether the certificate can be trusted
+
+ // -- First, verify that the the last certificate in the chain was
+ // -- issued by a third-party that we trust
+ ref <X509Certificate> lastCert =
+ chain->getAt(chain->getCount() - 1).dynamicCast <X509Certificate>();
+
+ bool trusted = false;
+
+ for (unsigned int i = 0 ; !trusted && i < m_x509RootCAs.size() ; ++i)
+ {
+ ref <X509Certificate> rootCa = m_x509RootCAs[i];
+
+ if (lastCert->verify(rootCa))
+ trusted = true;
+ }
+
+ // -- Next, if the issuer certificate cannot be verified against
+ // -- root CAs, compare the subject's certificate against the
+ // -- trusted certificates
+ ref <X509Certificate> firstCert =
+ chain->getAt(0).dynamicCast <X509Certificate>();
+
+ for (unsigned int i = 0 ; !trusted && i < m_x509TrustedCerts.size() ; ++i)
+ {
+ ref <X509Certificate> cert = m_x509TrustedCerts[i];
+
+ if (firstCert->equals(cert))
+ trusted = true;
+ }
+
+ if (!trusted)
+ {
+ throw exceptions::certificate_verification_exception
+ ("Cannot verify certificate against trusted certificates.");
+ }
+}
+
+
+void defaultCertificateVerifier::setX509RootCAs
+ (const std::vector <ref <X509Certificate> >& caCerts)
+{
+ m_x509RootCAs = caCerts;
+}
+
+
+void defaultCertificateVerifier::setX509TrustedCerts
+ (const std::vector <ref <X509Certificate> >& trustedCerts)
+{
+ m_x509TrustedCerts = trustedCerts;
+}
+
+
+} // tls
+} // net
+} // vmime
+
diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp
index 2941e5f2..04fa3308 100644
--- a/src/utility/stream.cpp
+++ b/src/utility/stream.cpp
@@ -117,6 +117,22 @@ void outputStreamStringAdapter::write(const value_type* const data, const size_t
+// outputStreamByteArrayAdapter
+
+outputStreamByteArrayAdapter::outputStreamByteArrayAdapter(byteArray& array)
+ : m_array(array)
+{
+ m_array.clear();
+}
+
+
+void outputStreamByteArrayAdapter::write(const value_type* const data, const size_type count)
+{
+ m_array.insert(m_array.end(), data, data + count);
+}
+
+
+
// inputStreamAdapter
inputStreamAdapter::inputStreamAdapter(std::istream& is)