diff options
| author | Vincent Richard <[email protected]> | 2013-12-29 10:02:12 +0100 |
|---|---|---|
| committer | Vincent Richard <[email protected]> | 2013-12-29 10:02:12 +0100 |
| commit | 152c6bed75598a6ca5efb7914701157270155833 (patch) | |
| tree | 8faced1d75a45c819630323da256248415992ed0 /src/net/imap/IMAPConnection.cpp | |
| parent | Merge branch 'master' of https://github.com/kisli/vmime (diff) | |
| download | vmime-152c6bed75598a6ca5efb7914701157270155833.tar.gz vmime-152c6bed75598a6ca5efb7914701157270155833.zip | |
Merged source and header files in directory structure. Got rid of SConstruct build.
Diffstat (limited to 'src/net/imap/IMAPConnection.cpp')
| -rw-r--r-- | src/net/imap/IMAPConnection.cpp | 814 |
1 files changed, 0 insertions, 814 deletions
diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp deleted file mode 100644 index 234c2b6a..00000000 --- a/src/net/imap/IMAPConnection.cpp +++ /dev/null @@ -1,814 +0,0 @@ -// -// VMime library (http://www.vmime.org) -// Copyright (C) 2002-2013 Vincent Richard <[email protected]> -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 3 of -// the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -// Linking this library statically or dynamically with other modules is making -// a combined work based on this library. Thus, the terms and conditions of -// the GNU General Public License cover the whole combination. -// - -#include "vmime/config.hpp" - - -#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP - - -#include "vmime/net/imap/IMAPTag.hpp" -#include "vmime/net/imap/IMAPConnection.hpp" -#include "vmime/net/imap/IMAPUtils.hpp" -#include "vmime/net/imap/IMAPStore.hpp" - -#include "vmime/exception.hpp" -#include "vmime/platform.hpp" - -#include "vmime/utility/stringUtils.hpp" - -#include "vmime/net/defaultConnectionInfos.hpp" - -#if VMIME_HAVE_SASL_SUPPORT - #include "vmime/security/sasl/SASLContext.hpp" -#endif // VMIME_HAVE_SASL_SUPPORT - -#if VMIME_HAVE_TLS_SUPPORT - #include "vmime/net/tls/TLSSession.hpp" - #include "vmime/net/tls/TLSSecuredConnectionInfos.hpp" -#endif // VMIME_HAVE_TLS_SUPPORT - -#include <sstream> - - -// Helpers for service properties -#define GET_PROPERTY(type, prop) \ - (m_store.lock()->getInfos().getPropertyValue <type>(getSession(), \ - dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) -#define HAS_PROPERTY(prop) \ - (m_store.lock()->getInfos().hasProperty(getSession(), \ - dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) - - -namespace vmime { -namespace net { -namespace imap { - - -IMAPConnection::IMAPConnection(shared_ptr <IMAPStore> store, shared_ptr <security::authenticator> auth) - : m_store(store), m_auth(auth), m_socket(null), m_parser(null), m_tag(null), - m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(null), - m_secured(false), m_firstTag(true), m_capabilitiesFetched(false), m_noModSeq(false) -{ -} - - -IMAPConnection::~IMAPConnection() -{ - try - { - if (isConnected()) - disconnect(); - else if (m_socket) - internalDisconnect(); - } - catch (vmime::exception&) - { - // Ignore - } -} - - -void IMAPConnection::connect() -{ - if (isConnected()) - throw exceptions::already_connected(); - - m_state = STATE_NONE; - m_hierarchySeparator = '\0'; - - const string address = GET_PROPERTY(string, PROPERTY_SERVER_ADDRESS); - const port_t port = GET_PROPERTY(port_t, PROPERTY_SERVER_PORT); - - shared_ptr <IMAPStore> store = m_store.lock(); - - // Create the time-out handler - if (store->getTimeoutHandlerFactory()) - m_timeoutHandler = store->getTimeoutHandlerFactory()->create(); - - // Create and connect the socket - m_socket = store->getSocketFactory()->create(m_timeoutHandler); - -#if VMIME_HAVE_TLS_SUPPORT - if (store->isIMAPS()) // dedicated port/IMAPS - { - shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create - (store->getCertificateVerifier(), - store->getSession()->getTLSProperties()); - - shared_ptr <tls::TLSSocket> tlsSocket = - tlsSession->getSocket(m_socket); - - m_socket = tlsSocket; - - m_secured = true; - m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos>(address, port, tlsSession, tlsSocket); - } - else -#endif // VMIME_HAVE_TLS_SUPPORT - { - m_cntInfos = make_shared <defaultConnectionInfos>(address, port); - } - - m_socket->connect(address, port); - - - m_tag = make_shared <IMAPTag>(); - m_parser = make_shared <IMAPParser>(m_tag, m_socket, m_timeoutHandler); - - - setState(STATE_NON_AUTHENTICATED); - - - // Connection greeting - // - // eg: C: <connection to server> - // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready - - std::auto_ptr <IMAPParser::greeting> greet(m_parser->readGreeting()); - bool needAuth = false; - - if (greet->resp_cond_bye()) - { - internalDisconnect(); - throw exceptions::connection_greeting_error(greet->getErrorLog()); - } - else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH) - { - needAuth = true; - } - - if (greet->resp_cond_auth()->resp_text()->resp_text_code() && - greet->resp_cond_auth()->resp_text()->resp_text_code()->capability_data()) - { - processCapabilityResponseData(greet->resp_cond_auth()->resp_text()->resp_text_code()->capability_data()); - } - -#if VMIME_HAVE_TLS_SUPPORT - // Setup secured connection, if requested - const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) - && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); - const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) - && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); - - if (!store->isIMAPS() && tls) // only if not IMAPS - { - try - { - startTLS(); - } - // Non-fatal error - catch (exceptions::command_error&) - { - if (tlsRequired) - { - m_state = STATE_NONE; - throw; - } - else - { - // TLS is not required, so don't bother - } - } - // Fatal error - catch (...) - { - m_state = STATE_NONE; - throw; - } - } -#endif // VMIME_HAVE_TLS_SUPPORT - - // Authentication - if (needAuth) - { - try - { - authenticate(); - } - catch (...) - { - m_state = STATE_NONE; - throw; - } - } - - // Get the hierarchy separator character - initHierarchySeparator(); - - // Switch to state "Authenticated" - setState(STATE_AUTHENTICATED); -} - - -void IMAPConnection::authenticate() -{ - getAuthenticator()->setService(m_store.lock()); - -#if VMIME_HAVE_SASL_SUPPORT - // First, try SASL authentication - if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) - { - try - { - authenticateSASL(); - return; - } - catch (exceptions::authentication_error& e) - { - if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) - { - // Can't fallback on normal authentication - internalDisconnect(); - throw e; - } - else - { - // Ignore, will try normal authentication - } - } - catch (exception& e) - { - internalDisconnect(); - throw e; - } - } -#endif // VMIME_HAVE_SASL_SUPPORT - - // Normal authentication - const string username = getAuthenticator()->getUsername(); - const string password = getAuthenticator()->getPassword(); - - send(true, "LOGIN " + IMAPUtils::quoteString(username) - + " " + IMAPUtils::quoteString(password), true); - - std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); - - if (resp->isBad()) - { - internalDisconnect(); - throw exceptions::command_error("LOGIN", resp->getErrorLog()); - } - else if (resp->response_done()->response_tagged()-> - resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) - { - internalDisconnect(); - throw exceptions::authentication_error(resp->getErrorLog()); - } - - // Server capabilities may change when logged in - if (!processCapabilityResponseData(resp.get())) - invalidateCapabilities(); -} - - -#if VMIME_HAVE_SASL_SUPPORT - -void IMAPConnection::authenticateSASL() -{ - if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) - throw exceptions::authentication_error("No SASL authenticator available."); - - const std::vector <string> capa = getCapabilities(); - std::vector <string> saslMechs; - - for (unsigned int i = 0 ; i < capa.size() ; ++i) - { - const string& x = capa[i]; - - if (x.length() > 5 && - (x[0] == 'A' || x[0] == 'a') && - (x[1] == 'U' || x[1] == 'u') && - (x[2] == 'T' || x[2] == 't') && - (x[3] == 'H' || x[3] == 'h') && - x[4] == '=') - { - saslMechs.push_back(string(x.begin() + 5, x.end())); - } - } - - if (saslMechs.empty()) - throw exceptions::authentication_error("No SASL mechanism available."); - - std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; - - shared_ptr <security::sasl::SASLContext> saslContext = - make_shared <security::sasl::SASLContext>(); - - for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) - { - try - { - mechList.push_back - (saslContext->createMechanism(saslMechs[i])); - } - catch (exceptions::no_such_mechanism&) - { - // Ignore mechanism - } - } - - if (mechList.empty()) - throw exceptions::authentication_error("No SASL mechanism available."); - - // Try to suggest a mechanism among all those supported - shared_ptr <security::sasl::SASLMechanism> suggestedMech = - saslContext->suggestMechanism(mechList); - - if (!suggestedMech) - throw exceptions::authentication_error("Unable to suggest SASL mechanism."); - - // Allow application to choose which mechanisms to use - mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> - getAcceptableMechanisms(mechList, suggestedMech); - - if (mechList.empty()) - throw exceptions::authentication_error("No SASL mechanism available."); - - // Try each mechanism in the list in turn - for (unsigned int i = 0 ; i < mechList.size() ; ++i) - { - shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; - - shared_ptr <security::sasl::SASLSession> saslSession = - saslContext->createSession("imap", getAuthenticator(), mech); - - saslSession->init(); - - send(true, "AUTHENTICATE " + mech->getName(), true); - - for (bool cont = true ; cont ; ) - { - std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); - - if (resp->response_done() && - resp->response_done()->response_tagged() && - resp->response_done()->response_tagged()->resp_cond_state()-> - status() == IMAPParser::resp_cond_state::OK) - { - m_socket = saslSession->getSecuredSocket(m_socket); - return; - } - else - { - std::vector <IMAPParser::continue_req_or_response_data*> - respDataList = resp->continue_req_or_response_data(); - - string response; - bool hasResponse = false; - - for (unsigned int i = 0 ; i < respDataList.size() ; ++i) - { - if (respDataList[i]->continue_req()) - { - response = respDataList[i]->continue_req()->resp_text()->text(); - hasResponse = true; - break; - } - } - - if (!hasResponse) - { - cont = false; - continue; - } - - byte_t* challenge = 0; - size_t challengeLen = 0; - - byte_t* resp = 0; - size_t respLen = 0; - - try - { - // Extract challenge - saslContext->decodeB64(response, &challenge, &challengeLen); - - // Prepare response - saslSession->evaluateChallenge - (challenge, challengeLen, &resp, &respLen); - - // Send response - send(false, saslContext->encodeB64(resp, respLen), true); - - // Server capabilities may change when logged in - invalidateCapabilities(); - } - catch (exceptions::sasl_exception& e) - { - if (challenge) - { - delete [] challenge; - challenge = NULL; - } - - if (resp) - { - delete [] resp; - resp = NULL; - } - - // Cancel SASL exchange - send(false, "*", true); - } - catch (...) - { - if (challenge) - delete [] challenge; - - if (resp) - delete [] resp; - - throw; - } - - if (challenge) - delete [] challenge; - - if (resp) - delete [] resp; - } - } - } - - throw exceptions::authentication_error - ("Could not authenticate using SASL: all mechanisms failed."); -} - -#endif // VMIME_HAVE_SASL_SUPPORT - - -#if VMIME_HAVE_TLS_SUPPORT - -void IMAPConnection::startTLS() -{ - try - { - send(true, "STARTTLS", true); - - std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); - - if (resp->isBad() || resp->response_done()->response_tagged()-> - resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) - { - throw exceptions::command_error - ("STARTTLS", resp->getErrorLog(), "bad response"); - } - - shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create - (m_store.lock()->getCertificateVerifier(), - m_store.lock()->getSession()->getTLSProperties()); - - shared_ptr <tls::TLSSocket> tlsSocket = - tlsSession->getSocket(m_socket); - - tlsSocket->handshake(m_timeoutHandler); - - m_socket = tlsSocket; - m_parser->setSocket(m_socket); - - m_secured = true; - m_cntInfos = make_shared <tls::TLSSecuredConnectionInfos> - (m_cntInfos->getHost(), m_cntInfos->getPort(), tlsSession, tlsSocket); - - // " Once TLS has been started, the client MUST discard cached - // information about server capabilities and SHOULD re-issue the - // CAPABILITY command. This is necessary to protect against - // man-in-the-middle attacks which alter the capabilities list prior - // to STARTTLS. " (RFC-2595) - invalidateCapabilities(); - } - catch (exceptions::command_error&) - { - // Non-fatal error - throw; - } - catch (exception&) - { - // Fatal error - internalDisconnect(); - throw; - } -} - -#endif // VMIME_HAVE_TLS_SUPPORT - - -const std::vector <string> IMAPConnection::getCapabilities() -{ - if (!m_capabilitiesFetched) - fetchCapabilities(); - - return m_capabilities; -} - - -bool IMAPConnection::hasCapability(const string& capa) -{ - if (!m_capabilitiesFetched) - fetchCapabilities(); - - const string normCapa = utility::stringUtils::toUpper(capa); - - for (size_t i = 0, n = m_capabilities.size() ; i < n ; ++i) - { - if (m_capabilities[i] == normCapa) - return true; - } - - return false; -} - - -void IMAPConnection::invalidateCapabilities() -{ - m_capabilities.clear(); - m_capabilitiesFetched = false; -} - - -void IMAPConnection::fetchCapabilities() -{ - send(true, "CAPABILITY", true); - - std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); - - if (resp->response_done()->response_tagged()-> - resp_cond_state()->status() == IMAPParser::resp_cond_state::OK) - { - processCapabilityResponseData(resp.get()); - } -} - - -bool IMAPConnection::processCapabilityResponseData(const IMAPParser::response* resp) -{ - const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = - resp->continue_req_or_response_data(); - - for (size_t i = 0 ; i < respDataList.size() ; ++i) - { - if (respDataList[i]->response_data() == NULL) - continue; - - const IMAPParser::capability_data* capaData = - respDataList[i]->response_data()->capability_data(); - - if (capaData == NULL) - continue; - - processCapabilityResponseData(capaData); - return true; - } - - return false; -} - - -void IMAPConnection::processCapabilityResponseData(const IMAPParser::capability_data* capaData) -{ - std::vector <string> res; - - std::vector <IMAPParser::capability*> caps = capaData->capabilities(); - - for (unsigned int j = 0 ; j < caps.size() ; ++j) - { - if (caps[j]->auth_type()) - res.push_back("AUTH=" + caps[j]->auth_type()->name()); - else - res.push_back(utility::stringUtils::toUpper(caps[j]->atom()->value())); - } - - m_capabilities = res; - m_capabilitiesFetched = true; -} - - -shared_ptr <security::authenticator> IMAPConnection::getAuthenticator() -{ - return m_auth; -} - - -bool IMAPConnection::isConnected() const -{ - return (m_socket && m_socket->isConnected() && - (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED)); -} - - -bool IMAPConnection::isSecuredConnection() const -{ - return m_secured; -} - - -shared_ptr <connectionInfos> IMAPConnection::getConnectionInfos() const -{ - return m_cntInfos; -} - - -void IMAPConnection::disconnect() -{ - if (!isConnected()) - throw exceptions::not_connected(); - - internalDisconnect(); -} - - -void IMAPConnection::internalDisconnect() -{ - if (isConnected()) - { - send(true, "LOGOUT", true); - - m_socket->disconnect(); - m_socket = null; - } - - m_timeoutHandler = null; - - m_state = STATE_LOGOUT; - - m_secured = false; - m_cntInfos = null; -} - - -void IMAPConnection::initHierarchySeparator() -{ - send(true, "LIST \"\" \"\"", true); - - std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); - - if (resp->isBad() || resp->response_done()->response_tagged()-> - resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) - { - internalDisconnect(); - throw exceptions::command_error("LIST", resp->getErrorLog(), "bad response"); - } - - const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = - resp->continue_req_or_response_data(); - - bool found = false; - - for (unsigned int i = 0 ; !found && i < respDataList.size() ; ++i) - { - if (respDataList[i]->response_data() == NULL) - continue; - - const IMAPParser::mailbox_data* mailboxData = - static_cast <const IMAPParser::response_data*> - (respDataList[i]->response_data())->mailbox_data(); - - if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST) - continue; - - if (mailboxData->mailbox_list()->quoted_char() != '\0') - { - m_hierarchySeparator = mailboxData->mailbox_list()->quoted_char(); - found = true; - } - } - - if (!found) // default - m_hierarchySeparator = '/'; -} - - -void IMAPConnection::send(bool tag, const string& what, bool end) -{ - if (tag && !m_firstTag) - ++(*m_tag); - -#if VMIME_DEBUG - std::ostringstream oss; - - if (tag) - { - oss << string(*m_tag); - oss << " "; - } - - oss << what; - - if (end) - oss << "\r\n"; - - m_socket->send(oss.str()); -#else - if (tag) - { - m_socket->send(*m_tag); - m_socket->send(" "); - } - - m_socket->send(what); - - if (end) - { - m_socket->send("\r\n"); - } -#endif - - if (tag) - m_firstTag = false; -} - - -void IMAPConnection::sendRaw(const byte_t* buffer, const size_t count) -{ - m_socket->sendRaw(buffer, count); -} - - -IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh) -{ - return (m_parser->readResponse(lh)); -} - - -IMAPConnection::ProtocolStates IMAPConnection::state() const -{ - return (m_state); -} - - -void IMAPConnection::setState(const ProtocolStates state) -{ - m_state = state; -} - - -char IMAPConnection::hierarchySeparator() const -{ - return (m_hierarchySeparator); -} - - -shared_ptr <const IMAPStore> IMAPConnection::getStore() const -{ - return m_store.lock(); -} - - -shared_ptr <IMAPStore> IMAPConnection::getStore() -{ - return m_store.lock(); -} - - -shared_ptr <session> IMAPConnection::getSession() -{ - return m_store.lock()->getSession(); -} - - -shared_ptr <const socket> IMAPConnection::getSocket() const -{ - return m_socket; -} - - -bool IMAPConnection::isMODSEQDisabled() const -{ - return m_noModSeq; -} - - -void IMAPConnection::disableMODSEQ() -{ - m_noModSeq = true; -} - - -} // imap -} // net -} // vmime - - -#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP - |
