From 24eff0069b3c1fc7de1dbfd566a39c0dd3154d66 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 16 Jan 2014 22:51:09 +0100 Subject: [PATCH 01/11] Added test for "+" not followed by space in continue_req. --- tests/net/imap/IMAPParserTest.cpp | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/net/imap/IMAPParserTest.cpp b/tests/net/imap/IMAPParserTest.cpp index 30e8f574..e598a548 100644 --- a/tests/net/imap/IMAPParserTest.cpp +++ b/tests/net/imap/IMAPParserTest.cpp @@ -31,6 +31,7 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest) VMIME_TEST_LIST_BEGIN VMIME_TEST(testExtraSpaceInCapaResponse) + VMIME_TEST(testContinueReqWithoutSpace) VMIME_TEST_LIST_END @@ -64,4 +65,40 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest) VASSERT_THROW("strict mode", parser->readResponse(/* literalHandler */ NULL), vmime::exceptions::invalid_response); } + // For Apple iCloud/Exchange IMAP server + void testContinueReqWithoutSpace() + { + // continue_req ::= "+" SPACE (resp_text / base64) + // + // Some servers do not send SPACE when response text is empty. + // IMAP parser should allow this in non-strict mode. + // + // Eg: + // + // C: a002 AUTHENTICATE xxx[CR][LF] + // S: +[CR][LF] + + vmime::shared_ptr socket = vmime::make_shared (); + vmime::shared_ptr toh = vmime::make_shared (); + + vmime::shared_ptr tag = + vmime::make_shared (); + + socket->localSend("+\r\n"); + + vmime::shared_ptr parser = + vmime::make_shared + (tag, vmime::dynamicCast (socket), toh); + + parser->setStrict(false); + VASSERT_NO_THROW("non-strict mode", parser->readResponse()); + + ++(*tag); + + socket->localSend("+\r\n"); + + parser->setStrict(true); + VASSERT_THROW("strict mode", parser->readResponse(), vmime::exceptions::invalid_response); + } + VMIME_TEST_SUITE_END From b6469f68a8c61da02d440cdd022c17cd8cdaeeaa Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 16:36:06 +0100 Subject: [PATCH 02/11] Fixed SMTP disconnection error caused by handshake failure. --- src/vmime/net/smtp/SMTPConnection.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp index e0b5bc68..574e5354 100644 --- a/src/vmime/net/smtp/SMTPConnection.cpp +++ b/src/vmime/net/smtp/SMTPConnection.cpp @@ -522,16 +522,19 @@ void SMTPConnection::disconnect() void SMTPConnection::internalDisconnect() { - try + if (isConnected()) { - sendRequest(SMTPCommand::QUIT()); + try + { + sendRequest(SMTPCommand::QUIT()); - // Do not wait for server response. This is contrary to the RFC, but - // some servers never send a response to a QUIT command. - } - catch (exception&) - { - // Not important + // Do not wait for server response. This is contrary to the RFC, but + // some servers never send a response to a QUIT command. + } + catch (exception&) + { + // Not important + } } m_socket->disconnect(); From 2afe340b7b4621b81c3bad10ead23ce50ad83baa Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 16:36:45 +0100 Subject: [PATCH 03/11] In SSL socket, use timeout handler of underlying socket. Throw exception when reading from/writing to disconnected SSL socket. --- src/vmime/exception.cpp | 13 +++ src/vmime/exception.hpp | 17 ++++ src/vmime/net/imap/IMAPConnection.cpp | 2 +- src/vmime/net/pop3/POP3Connection.cpp | 2 +- src/vmime/net/socket.hpp | 6 ++ src/vmime/net/tls/TLSSocket.hpp | 2 +- src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp | 36 +++++--- src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp | 5 +- .../net/tls/openssl/TLSSocket_OpenSSL.cpp | 87 +++++++++++-------- .../net/tls/openssl/TLSSocket_OpenSSL.hpp | 6 +- src/vmime/platforms/posix/posixSocket.cpp | 6 ++ src/vmime/platforms/posix/posixSocket.hpp | 2 + src/vmime/platforms/windows/windowsSocket.cpp | 6 ++ src/vmime/platforms/windows/windowsSocket.hpp | 2 + src/vmime/security/sasl/SASLSocket.cpp | 6 ++ src/vmime/security/sasl/SASLSocket.hpp | 2 + tests/testUtils.cpp | 6 ++ tests/testUtils.hpp | 2 + 18 files changed, 153 insertions(+), 55 deletions(-) diff --git a/src/vmime/exception.cpp b/src/vmime/exception.cpp index 042ac4f4..dff7d497 100644 --- a/src/vmime/exception.cpp +++ b/src/vmime/exception.cpp @@ -327,6 +327,19 @@ exception* socket_exception::clone() const { return new socket_exception(*this); const char* socket_exception::name() const throw() { return "socket_exception"; } +// +// socket_not_connected_exception +// + +socket_not_connected_exception::~socket_not_connected_exception() throw() {} +socket_not_connected_exception::socket_not_connected_exception(const string& what, const exception& other) + : socket_exception(what.empty() + ? "Socket is not connected." : what, other) {} + +exception* socket_not_connected_exception::clone() const { return new socket_not_connected_exception(*this); } +const char* socket_not_connected_exception::name() const throw() { return "socket_not_connected_exception"; } + + // // connection_error // diff --git a/src/vmime/exception.hpp b/src/vmime/exception.hpp index e2afcc62..3c547756 100644 --- a/src/vmime/exception.hpp +++ b/src/vmime/exception.hpp @@ -370,6 +370,23 @@ public: }; +/** Socket not connected: you are trying to write to/read from a socket which + * is not connected to a peer. + */ + +class VMIME_EXPORT socket_not_connected_exception : public socket_exception +{ +public: + + socket_not_connected_exception(const string& what = "", const exception& other = NO_EXCEPTION); + ~socket_not_connected_exception() throw(); + + exception* clone() const; + const char* name() const throw(); + +}; + + /** Error while connecting to the server: this may be a DNS resolution error * or a connection error (for example, time-out while connecting). */ diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index bab4e58b..7a90b142 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -482,7 +482,7 @@ void IMAPConnection::startTLS() shared_ptr tlsSocket = tlsSession->getSocket(m_socket); - tlsSocket->handshake(m_timeoutHandler); + tlsSocket->handshake(); m_socket = tlsSocket; m_parser->setSocket(m_socket); diff --git a/src/vmime/net/pop3/POP3Connection.cpp b/src/vmime/net/pop3/POP3Connection.cpp index 5fa923f4..283cc91b 100644 --- a/src/vmime/net/pop3/POP3Connection.cpp +++ b/src/vmime/net/pop3/POP3Connection.cpp @@ -552,7 +552,7 @@ void POP3Connection::startTLS() shared_ptr tlsSocket = tlsSession->getSocket(m_socket); - tlsSocket->handshake(m_timeoutHandler); + tlsSocket->handshake(); m_socket = tlsSocket; diff --git a/src/vmime/net/socket.hpp b/src/vmime/net/socket.hpp index 537c34bb..7f878a73 100644 --- a/src/vmime/net/socket.hpp +++ b/src/vmime/net/socket.hpp @@ -141,6 +141,12 @@ public: */ virtual const string getPeerAddress() const = 0; + /** Return the timeout handler associated with this socket. + * + * @return timeout handler, or NULL if none is set + */ + virtual shared_ptr getTimeoutHandler() = 0; + protected: socket() { } diff --git a/src/vmime/net/tls/TLSSocket.hpp b/src/vmime/net/tls/TLSSocket.hpp index e2668ad4..ec3a83ef 100644 --- a/src/vmime/net/tls/TLSSocket.hpp +++ b/src/vmime/net/tls/TLSSocket.hpp @@ -67,7 +67,7 @@ public: * during the negociation process, exceptions::operation_timed_out * if a time-out occurs */ - virtual void handshake(shared_ptr toHandler = null) = 0; + virtual void handshake() = 0; /** Return the peer's certificate (chain) as sent by the peer. * diff --git a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp index edc8811a..13b7eb24 100644 --- a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp +++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -87,11 +87,17 @@ TLSSocket_GnuTLS::~TLSSocket_GnuTLS() void TLSSocket_GnuTLS::connect(const string& address, const port_t port) { - m_wrapped->connect(address, port); + try + { + m_wrapped->connect(address, port); - handshake(null); - - m_connected = true; + handshake(); + } + catch (...) + { + disconnect(); + throw; + } } @@ -132,6 +138,12 @@ const string TLSSocket_GnuTLS::getPeerAddress() const } +shared_ptr TLSSocket_GnuTLS::getTimeoutHandler() +{ + return m_wrapped->getTimeoutHandler(); +} + + void TLSSocket_GnuTLS::receive(string& buffer) { const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); @@ -239,14 +251,15 @@ unsigned int TLSSocket_GnuTLS::getStatus() const } -void TLSSocket_GnuTLS::handshake(shared_ptr toHandler) +void TLSSocket_GnuTLS::handshake() { + shared_ptr toHandler = m_wrapped->getTimeoutHandler(); + if (toHandler) toHandler->resetTimeOut(); // Start handshaking process m_handshaking = true; - m_toHandler = toHandler; try { @@ -280,13 +293,10 @@ void TLSSocket_GnuTLS::handshake(shared_ptr toHandler) catch (...) { m_handshaking = false; - m_toHandler = null; - throw; } m_handshaking = false; - m_toHandler = null; // Verify server's certificate(s) shared_ptr certs = getPeerCertificates(); @@ -338,6 +348,8 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc // returns -1 and errno is set to EGAIN... if (sok->m_handshaking) { + shared_ptr toHandler = sok->m_wrapped->getTimeoutHandler(); + while (true) { const ssize_t ret = static_cast @@ -355,12 +367,12 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc } // Check whether the time-out delay is elapsed - if (sok->m_toHandler && sok->m_toHandler->isTimeOut()) + if (toHandler && toHandler->isTimeOut()) { - if (!sok->m_toHandler->handleTimeOut()) + if (!toHandler->handleTimeOut()) throw exceptions::operation_timed_out(); - sok->m_toHandler->resetTimeOut(); + toHandler->resetTimeOut(); } } } diff --git a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp index 885fac13..ddba9d0e 100644 --- a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp +++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -54,7 +54,7 @@ public: ~TLSSocket_GnuTLS(); - void handshake(shared_ptr toHandler = null); + void handshake(); shared_ptr getPeerCertificates() const; @@ -78,6 +78,8 @@ public: const string getPeerName() const; const string getPeerAddress() const; + shared_ptr getTimeoutHandler(); + private: void internalThrow(); @@ -99,7 +101,6 @@ private: byte_t m_buffer[65536]; bool m_handshaking; - shared_ptr m_toHandler; exception* m_ex; diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp index 9857b8fb..595a0091 100644 --- a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp +++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -87,12 +87,6 @@ TLSSocket_OpenSSL::~TLSSocket_OpenSSL() try { disconnect(); - - if (m_ssl) - { - SSL_free(m_ssl); - m_ssl = 0; - } } catch (...) { @@ -130,32 +124,41 @@ void TLSSocket_OpenSSL::createSSLHandle() void TLSSocket_OpenSSL::connect(const string& address, const port_t port) { - m_wrapped->connect(address, port); + try + { + m_wrapped->connect(address, port); - createSSLHandle(); + createSSLHandle(); - handshake(null); - - m_connected = true; + handshake(); + } + catch (...) + { + disconnect(); + throw; + } } void TLSSocket_OpenSSL::disconnect() { + if (m_ssl) + { + // Don't shut down the socket more than once. + int shutdownState = SSL_get_shutdown(m_ssl); + bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; + + if (!shutdownSent) + SSL_shutdown(m_ssl); + + SSL_free(m_ssl); + m_ssl = 0; + } + if (m_connected) { - if (m_ssl) - { - // Don't shut down the socket more than once. - int shutdownState = SSL_get_shutdown(m_ssl); - bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; - - if (!shutdownSent) - SSL_shutdown(m_ssl); - } - - m_wrapped->disconnect(); m_connected = false; + m_wrapped->disconnect(); } } @@ -184,6 +187,12 @@ const string TLSSocket_OpenSSL::getPeerAddress() const } +shared_ptr TLSSocket_OpenSSL::getTimeoutHandler() +{ + return m_wrapped->getTimeoutHandler(); +} + + void TLSSocket_OpenSSL::receive(string& buffer) { const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); @@ -209,6 +218,9 @@ void TLSSocket_OpenSSL::send(const char* str) size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) { + if (!m_ssl) + throw exceptions::socket_not_connected_exception(); + m_status &= ~STATUS_WOULDBLOCK; int rc = SSL_read(m_ssl, buffer, static_cast (count)); @@ -235,6 +247,9 @@ size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) { + if (!m_ssl) + throw exceptions::socket_not_connected_exception(); + m_status &= ~STATUS_WOULDBLOCK; for (size_t size = count ; size > 0 ; ) @@ -264,6 +279,9 @@ void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + if (!m_ssl) + throw exceptions::socket_not_connected_exception(); + m_status &= ~STATUS_WOULDBLOCK; int rc = SSL_write(m_ssl, buffer, static_cast (count)); @@ -288,14 +306,17 @@ size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t } -void TLSSocket_OpenSSL::handshake(shared_ptr toHandler) +void TLSSocket_OpenSSL::handshake() { + if (!m_ssl) + throw exceptions::socket_not_connected_exception(); + + shared_ptr toHandler = m_wrapped->getTimeoutHandler(); + if (toHandler) toHandler->resetTimeOut(); // Start handshaking process - m_toHandler = toHandler; - if (!m_ssl) createSSLHandle(); @@ -318,25 +339,20 @@ void TLSSocket_OpenSSL::handshake(shared_ptr toHandler) } // Check whether the time-out delay is elapsed - if (m_toHandler && m_toHandler->isTimeOut()) + if (toHandler && toHandler->isTimeOut()) { - if (!m_toHandler->handleTimeOut()) + if (!toHandler->handleTimeOut()) throw exceptions::operation_timed_out(); - m_toHandler->resetTimeOut(); + toHandler->resetTimeOut(); } } } catch (...) { - SSL_free(m_ssl); - m_ssl = 0; - m_toHandler = null; throw; } - m_toHandler = null; - // Verify server's certificate(s) shared_ptr certs = getPeerCertificates(); @@ -401,6 +417,8 @@ void TLSSocket_OpenSSL::handleError(int rc) switch (sslError) { case SSL_ERROR_ZERO_RETURN: + + disconnect(); return; case SSL_ERROR_SYSCALL: @@ -413,8 +431,7 @@ void TLSSocket_OpenSSL::handleError(int rc) } else { - vmime::string msg; - std::ostringstream oss(msg); + std::ostringstream oss; oss << "The BIO reported an error: " << rc; oss.flush(); throw exceptions::tls_exception(oss.str()); diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp index 410fffcf..5fbed19d 100644 --- a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp +++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -58,7 +58,7 @@ public: ~TLSSocket_OpenSSL(); - void handshake(shared_ptr toHandler = null); + void handshake(); shared_ptr getPeerCertificates() const; @@ -82,6 +82,8 @@ public: const string getPeerName() const; const string getPeerAddress() const; + shared_ptr getTimeoutHandler(); + private: static BIO_METHOD sm_customBIOMethod; @@ -108,8 +110,6 @@ private: byte_t m_buffer[65536]; - shared_ptr m_toHandler; - SSL* m_ssl; unsigned long m_status; diff --git a/src/vmime/platforms/posix/posixSocket.cpp b/src/vmime/platforms/posix/posixSocket.cpp index ab434116..e7eba9f1 100644 --- a/src/vmime/platforms/posix/posixSocket.cpp +++ b/src/vmime/platforms/posix/posixSocket.cpp @@ -654,6 +654,12 @@ unsigned int posixSocket::getStatus() const } +shared_ptr posixSocket::getTimeoutHandler() +{ + return m_timeoutHandler; +} + + // // posixSocketFactory diff --git a/src/vmime/platforms/posix/posixSocket.hpp b/src/vmime/platforms/posix/posixSocket.hpp index 4ec3edec..5d29d710 100644 --- a/src/vmime/platforms/posix/posixSocket.hpp +++ b/src/vmime/platforms/posix/posixSocket.hpp @@ -65,6 +65,8 @@ public: const string getPeerName() const; const string getPeerAddress() const; + shared_ptr getTimeoutHandler(); + protected: static void throwSocketError(const int err); diff --git a/src/vmime/platforms/windows/windowsSocket.cpp b/src/vmime/platforms/windows/windowsSocket.cpp index cb96481a..bd20e5d4 100644 --- a/src/vmime/platforms/windows/windowsSocket.cpp +++ b/src/vmime/platforms/windows/windowsSocket.cpp @@ -460,6 +460,12 @@ void windowsSocket::waitForData(const WaitOpType t, bool& timedOut) } +shared_ptr windowsSocket::getTimeoutHandler() +{ + return m_timeoutHandler; +} + + // // posixSocketFactory diff --git a/src/vmime/platforms/windows/windowsSocket.hpp b/src/vmime/platforms/windows/windowsSocket.hpp index cb8a6e67..31e1488b 100644 --- a/src/vmime/platforms/windows/windowsSocket.hpp +++ b/src/vmime/platforms/windows/windowsSocket.hpp @@ -69,6 +69,8 @@ public: const string getPeerName() const; const string getPeerAddress() const; + shared_ptr getTimeoutHandler(); + protected: void throwSocketError(const int err); diff --git a/src/vmime/security/sasl/SASLSocket.cpp b/src/vmime/security/sasl/SASLSocket.cpp index 12d634c2..541fc904 100644 --- a/src/vmime/security/sasl/SASLSocket.cpp +++ b/src/vmime/security/sasl/SASLSocket.cpp @@ -96,6 +96,12 @@ const string SASLSocket::getPeerAddress() const } +shared_ptr SASLSocket::getTimeoutHandler() +{ + return m_wrapped->getTimeoutHandler(); +} + + void SASLSocket::receive(string& buffer) { const size_t n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer)); diff --git a/src/vmime/security/sasl/SASLSocket.hpp b/src/vmime/security/sasl/SASLSocket.hpp index e52911b4..d2d82411 100644 --- a/src/vmime/security/sasl/SASLSocket.hpp +++ b/src/vmime/security/sasl/SASLSocket.hpp @@ -73,6 +73,8 @@ public: const string getPeerName() const; const string getPeerAddress() const; + shared_ptr getTimeoutHandler(); + private: shared_ptr m_session; diff --git a/tests/testUtils.cpp b/tests/testUtils.cpp index 437b476b..ee642bea 100644 --- a/tests/testUtils.cpp +++ b/tests/testUtils.cpp @@ -79,6 +79,12 @@ const vmime::string testSocket::getPeerAddress() const } +vmime::shared_ptr testSocket::getTimeoutHandler() +{ + return vmime::null; +} + + void testSocket::receive(vmime::string& buffer) { buffer = m_inBuffer; diff --git a/tests/testUtils.hpp b/tests/testUtils.hpp index f0ecf454..9e72158a 100644 --- a/tests/testUtils.hpp +++ b/tests/testUtils.hpp @@ -263,6 +263,8 @@ public: const vmime::string getPeerName() const; const vmime::string getPeerAddress() const; + vmime::shared_ptr getTimeoutHandler(); + /** Send data to client. * * @param buffer data to send From 2e4bdab7047a5ccffd2368fd400f6bc0876c94cf Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 16:45:01 +0100 Subject: [PATCH 04/11] Fixed argument to handshake(). --- src/vmime/net/smtp/SMTPConnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp index 574e5354..d7c04345 100644 --- a/src/vmime/net/smtp/SMTPConnection.cpp +++ b/src/vmime/net/smtp/SMTPConnection.cpp @@ -487,7 +487,7 @@ void SMTPConnection::startTLS() shared_ptr tlsSocket = tlsSession->getSocket(m_socket); - tlsSocket->handshake(m_timeoutHandler); + tlsSocket->handshake(); m_socket = tlsSocket; From 74e8e4792651ea74fcdb6786e58deea563e2fb0f Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 16:45:28 +0100 Subject: [PATCH 05/11] Fixed possible crash when COPYUID/APPENDUID is not supported by the server. --- src/vmime/net/imap/IMAPFolder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp index 343ec35e..3af9225a 100644 --- a/src/vmime/net/imap/IMAPFolder.cpp +++ b/src/vmime/net/imap/IMAPFolder.cpp @@ -1124,7 +1124,7 @@ messageSet IMAPFolder::addMessage const IMAPParser::resp_text_code* respTextCode = finalResp->response_done()->response_tagged()->resp_cond_state()->resp_text()->resp_text_code(); - if (respTextCode->type() == IMAPParser::resp_text_code::APPENDUID) + if (respTextCode && respTextCode->type() == IMAPParser::resp_text_code::APPENDUID) return IMAPUtils::buildMessageSet(respTextCode->uid_set()); return messageSet::empty(); @@ -1266,7 +1266,7 @@ messageSet IMAPFolder::copyMessages(const folder::path& dest, const messageSet& const IMAPParser::resp_text_code* respTextCode = resp->response_done()->response_tagged()->resp_cond_state()->resp_text()->resp_text_code(); - if (respTextCode->type() == IMAPParser::resp_text_code::COPYUID) + if (respTextCode && respTextCode->type() == IMAPParser::resp_text_code::COPYUID) return IMAPUtils::buildMessageSet(respTextCode->uid_set2()); return messageSet::empty(); From 5ec4ea4aa25815defa465b504f4b286309f70795 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 16:52:49 +0100 Subject: [PATCH 06/11] Better error message. --- src/vmime/textPartFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vmime/textPartFactory.cpp b/src/vmime/textPartFactory.cpp index 85fea6e4..846a6605 100644 --- a/src/vmime/textPartFactory.cpp +++ b/src/vmime/textPartFactory.cpp @@ -62,7 +62,7 @@ shared_ptr textPartFactory::create(const mediaType& type) return ((*it).second)(); } - throw exceptions::no_factory_available(); + throw exceptions::no_factory_available("No 'textPart' class registered for media type '" + type.generate() + "'."); } From 58bad6e4887bd41c17f3b89a70a1837356a12ebf Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 17:25:25 +0100 Subject: [PATCH 07/11] Path to/from string conversion. --- src/vmime/utility/path.cpp | 47 ++++++++++++++++++++++++++++++++++++++ src/vmime/utility/path.hpp | 19 +++++++++++++++ tests/utility/pathTest.cpp | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/src/vmime/utility/path.cpp b/src/vmime/utility/path.cpp index 59685866..aaa3192e 100644 --- a/src/vmime/utility/path.cpp +++ b/src/vmime/utility/path.cpp @@ -261,5 +261,52 @@ path::component& path::getComponentAt(const size_t pos) } +// static +path path::fromString(const string& str, const string& sep, const charset& cset) +{ + path p; + + size_t start = 0; + size_t end = 0; + + do + { + end = str.find(sep, start); + + string comp; + + if (end == string::npos) + comp = str.substr(start); + else + comp = str.substr(start, end - start); + + // Skip leading or trailing separators + if (comp.length()) + p.appendComponent(component(comp, cset)); + + start = end + 1; + } + while (end != string::npos); + + return p; +} + + +const string path::toString(const string& sep, const charset& cset) +{ + string str; + + for (size_t i = 0 ; i < m_list.size() ; ++i) + { + if (i != 0) + str += sep; + + str += m_list[i].getConvertedText(cset); + } + + return str; +} + + } // utility } // vmime diff --git a/src/vmime/utility/path.hpp b/src/vmime/utility/path.hpp index 203da246..02fd9ce6 100644 --- a/src/vmime/utility/path.hpp +++ b/src/vmime/utility/path.hpp @@ -158,6 +158,25 @@ public: */ void renameParent(const path& oldPath, const path& newPath); + /** Construct a new path from a string. + * + * @param str string representation of the path + * @param sep separator string (eg: "/") + * @param cset charset in which the path is encoded (use the value returned by + * vmime::charset::getLocalCharset() to use the default charset of your system) + * @return a new path corresponding to the specified string + */ + static path fromString(const string& str, const string& sep, const charset& cset); + + /** Returns a string representation of this path. + * + * @param sep separator string (eg: "/") + * @param cset charset in which to encode the components (use the value returned by + * vmime::charset::getLocalCharset() to use the default charset of your system) + * @return a string representing this path + */ + const string toString(const string& sep, const charset& cset); + private: list m_list; diff --git a/tests/utility/pathTest.cpp b/tests/utility/pathTest.cpp index d0c1c091..ef1b773d 100644 --- a/tests/utility/pathTest.cpp +++ b/tests/utility/pathTest.cpp @@ -53,6 +53,10 @@ VMIME_TEST_SUITE_BEGIN(utilityPathTest) VMIME_TEST(testIsParentOf_EquivalentCharset) VMIME_TEST(testRenameParent) + + VMIME_TEST(testFromString) + VMIME_TEST(testFromString_IgnoreLeadingOrTrailingSep) + VMIME_TEST(testToString) VMIME_TEST_LIST_END @@ -313,5 +317,41 @@ VMIME_TEST_SUITE_BEGIN(utilityPathTest) VASSERT_EQ("6", "d", p.getComponentAt(4).getBuffer()); } + void testFromString() + { + path p = path::fromString("ab/cde/f", "/", vmime::charset("my-charset")); + + VASSERT_EQ("count", 3, p.getSize()); + VASSERT_EQ("buffer1", "ab", p.getComponentAt(0).getBuffer()); + VASSERT_EQ("charset1", "my-charset", p.getComponentAt(0).getCharset().getName()); + VASSERT_EQ("buffer2", "cde", p.getComponentAt(1).getBuffer()); + VASSERT_EQ("charset2", "my-charset", p.getComponentAt(1).getCharset().getName()); + VASSERT_EQ("buffer3", "f", p.getComponentAt(2).getBuffer()); + VASSERT_EQ("charset3", "my-charset", p.getComponentAt(2).getCharset().getName()); + } + + void testFromString_IgnoreLeadingOrTrailingSep() + { + path p = path::fromString("//ab/cde/f////", "/", vmime::charset("my-charset")); + + VASSERT_EQ("count", 3, p.getSize()); + VASSERT_EQ("buffer1", "ab", p.getComponentAt(0).getBuffer()); + VASSERT_EQ("charset1", "my-charset", p.getComponentAt(0).getCharset().getName()); + VASSERT_EQ("buffer2", "cde", p.getComponentAt(1).getBuffer()); + VASSERT_EQ("charset2", "my-charset", p.getComponentAt(1).getCharset().getName()); + VASSERT_EQ("buffer3", "f", p.getComponentAt(2).getBuffer()); + VASSERT_EQ("charset3", "my-charset", p.getComponentAt(2).getCharset().getName()); + } + + void testToString() + { + path p; + p.appendComponent(comp("ab")); + p.appendComponent(comp("cde")); + p.appendComponent(comp("f")); + + VASSERT_EQ("string", "ab/cde/f", p.toString("/", vmime::charset("us-ascii"))); + } + VMIME_TEST_SUITE_END From 6eaf199c69f81f7034f0480a8ab70241bc5f8f81 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 17:27:23 +0100 Subject: [PATCH 08/11] Do not flag existing messages as recent. --- src/vmime/net/pop3/POP3Message.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vmime/net/pop3/POP3Message.cpp b/src/vmime/net/pop3/POP3Message.cpp index 08523611..ff1aa88f 100644 --- a/src/vmime/net/pop3/POP3Message.cpp +++ b/src/vmime/net/pop3/POP3Message.cpp @@ -95,7 +95,7 @@ bool POP3Message::isExpunged() const int POP3Message::getFlags() const { - int flags = FLAG_RECENT; + int flags = 0; if (m_deleted) flags |= FLAG_DELETED; From 2e042dd65fa7d5042797abd9abf68382188b7da5 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 17:39:49 +0100 Subject: [PATCH 09/11] Calling getAttributes() does not require the folder to be open. --- src/vmime/net/imap/IMAPFolder.cpp | 3 --- src/vmime/net/pop3/POP3Folder.cpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp index 3af9225a..25b7d760 100644 --- a/src/vmime/net/imap/IMAPFolder.cpp +++ b/src/vmime/net/imap/IMAPFolder.cpp @@ -92,9 +92,6 @@ int IMAPFolder::getMode() const const folderAttributes IMAPFolder::getAttributes() { - if (!isOpen()) - throw exceptions::illegal_state("Folder not open"); - // Root folder if (m_path.isEmpty()) { diff --git a/src/vmime/net/pop3/POP3Folder.cpp b/src/vmime/net/pop3/POP3Folder.cpp index 56aeae6e..354dad2a 100644 --- a/src/vmime/net/pop3/POP3Folder.cpp +++ b/src/vmime/net/pop3/POP3Folder.cpp @@ -83,9 +83,6 @@ int POP3Folder::getMode() const const folderAttributes POP3Folder::getAttributes() { - if (!isOpen()) - throw exceptions::illegal_state("Folder not open"); - folderAttributes attribs; if (m_path.isEmpty()) From ab5a0b75db1dd72a14860f8d9473679ffbabff9d Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 22:50:37 +0100 Subject: [PATCH 10/11] SASL is the only authentication method supported in SMTP (no fallback). --- src/vmime/net/smtp/SMTPConnection.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp index d7c04345..bb5e3c2e 100644 --- a/src/vmime/net/smtp/SMTPConnection.cpp +++ b/src/vmime/net/smtp/SMTPConnection.cpp @@ -284,7 +284,7 @@ void SMTPConnection::authenticate() getAuthenticator()->setService(m_transport.lock()); #if VMIME_HAVE_SASL_SUPPORT - // First, try SASL authentication + // Try SASL authentication if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) { try @@ -294,19 +294,6 @@ void SMTPConnection::authenticate() m_authenticated = true; return; } - catch (exceptions::authentication_error& e) - { - if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) - { - // Can't fallback on normal authentication - internalDisconnect(); - throw e; - } - else - { - // Ignore, will try normal authentication - } - } catch (exception& e) { internalDisconnect(); From 6c9a32d314da1466a2d30f3b7293b740774ba803 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jan 2014 23:27:37 +0100 Subject: [PATCH 11/11] Use appropriate syntax for rethrowing exceptions. --- src/vmime/net/imap/IMAPConnection.cpp | 8 ++++---- src/vmime/net/pop3/POP3Connection.cpp | 8 ++++---- src/vmime/net/smtp/SMTPConnection.cpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index 7a90b142..5e6f6f8f 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -234,23 +234,23 @@ void IMAPConnection::authenticate() authenticateSASL(); return; } - catch (exceptions::authentication_error& e) + catch (exceptions::authentication_error&) { if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) { // Can't fallback on normal authentication internalDisconnect(); - throw e; + throw; } else { // Ignore, will try normal authentication } } - catch (exception& e) + catch (exception&) { internalDisconnect(); - throw e; + throw; } } #endif // VMIME_HAVE_SASL_SUPPORT diff --git a/src/vmime/net/pop3/POP3Connection.cpp b/src/vmime/net/pop3/POP3Connection.cpp index 283cc91b..f5a1a448 100644 --- a/src/vmime/net/pop3/POP3Connection.cpp +++ b/src/vmime/net/pop3/POP3Connection.cpp @@ -233,23 +233,23 @@ void POP3Connection::authenticate(const messageId& randomMID) m_authenticated = true; return; } - catch (exceptions::authentication_error& e) + catch (exceptions::authentication_error&) { if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) { // Can't fallback on APOP/normal authentication internalDisconnect(); - throw e; + throw; } else { // Ignore, will try APOP/normal authentication } } - catch (exception& e) + catch (exception&) { internalDisconnect(); - throw e; + throw; } } #endif // VMIME_HAVE_SASL_SUPPORT diff --git a/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp index bb5e3c2e..a45f9149 100644 --- a/src/vmime/net/smtp/SMTPConnection.cpp +++ b/src/vmime/net/smtp/SMTPConnection.cpp @@ -294,10 +294,10 @@ void SMTPConnection::authenticate() m_authenticated = true; return; } - catch (exception& e) + catch (exception&) { internalDisconnect(); - throw e; + throw; } } #endif // VMIME_HAVE_SASL_SUPPORT