diff --git a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp index 35e1d415..477f655b 100644 --- a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp +++ b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -53,7 +53,7 @@ ref TLSSocket::wrap(ref session, ref sok) TLSSocket_GnuTLS::TLSSocket_GnuTLS(ref session, ref sok) : m_session(session), m_wrapped(sok), m_connected(false), - m_handshaking(false), m_ex(NULL) + m_handshaking(false), m_ex(NULL), m_status(0) { gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this); @@ -131,6 +131,8 @@ void TLSSocket_GnuTLS::send(const string& buffer) TLSSocket::size_type TLSSocket_GnuTLS::receiveRaw(char* buffer, const size_type count) { + m_status &= ~STATUS_WOULDBLOCK; + const ssize_t ret = gnutls_record_recv (*m_session->m_gnutlsSession, buffer, static_cast (count)); @@ -141,23 +143,67 @@ TLSSocket::size_type TLSSocket_GnuTLS::receiveRaw(char* buffer, const size_type if (ret < 0) { if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; return 0; + } TLSSession_GnuTLS::throwTLSException("gnutls_record_recv", ret); } - return static_cast (ret); + return static_cast (ret); } void TLSSocket_GnuTLS::sendRaw(const char* buffer, const size_type count) { - gnutls_record_send + ssize_t ret = gnutls_record_send (*m_session->m_gnutlsSession, buffer, static_cast (count)); if (m_ex) internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; + return; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", ret); + } +} + + +TLSSocket::size_type TLSSocket_GnuTLS::sendRawNonBlocking(const char* buffer, const size_type count) +{ + ssize_t ret = gnutls_record_send + (*m_session->m_gnutlsSession, + buffer, static_cast (count)); + + if (m_ex) + internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", ret); + } + + return static_cast (ret); +} + + +unsigned int TLSSocket_GnuTLS::getStatus() const +{ + return m_status | m_wrapped->getStatus(); } @@ -290,8 +336,8 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc (reinterpret_cast (data), static_cast (len))); - if (n == 0) - return GNUTLS_E_AGAIN; // This seems like a hack, really... + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) + return GNUTLS_E_AGAIN; return n; } diff --git a/src/net/tls/openssl/TLSSession_OpenSSL.cpp b/src/net/tls/openssl/TLSSession_OpenSSL.cpp index 5d7e1052..248322d4 100755 --- a/src/net/tls/openssl/TLSSession_OpenSSL.cpp +++ b/src/net/tls/openssl/TLSSession_OpenSSL.cpp @@ -62,6 +62,9 @@ TLSSession_OpenSSL::TLSSession_OpenSSL(ref TLSSocket::wrap(ref session, ref sok) TLSSocket_OpenSSL::TLSSocket_OpenSSL(ref session, ref sok) - : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0) + : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0), m_ex(NULL) { } @@ -162,8 +162,10 @@ TLSSocket::size_type TLSSocket_OpenSSL::getBlockSize() const void TLSSocket_OpenSSL::receive(string& buffer) { - const int size = receiveRaw(m_buffer, sizeof(m_buffer)); - buffer = vmime::string(m_buffer, size); + const size_type size = receiveRaw(m_buffer, sizeof(m_buffer)); + + if (size >= 0) + buffer = vmime::string(m_buffer, size); } @@ -175,19 +177,35 @@ void TLSSocket_OpenSSL::send(const string& buffer) TLSSocket::size_type TLSSocket_OpenSSL::receiveRaw(char* buffer, const size_type count) { - int rc = SSL_read(m_ssl, buffer, count); + int rc = SSL_read(m_ssl, buffer, static_cast (count)); handleError(rc); + + if (rc < 0) + return 0; + return rc; } void TLSSocket_OpenSSL::sendRaw(const char* buffer, const size_type count) { - int rc = SSL_write(m_ssl, buffer, count); + int rc = SSL_write(m_ssl, buffer, static_cast (count)); handleError(rc); } +TLSSocket_OpenSSL::size_type TLSSocket_OpenSSL::sendRawNonBlocking(const char* buffer, const size_type count) +{ + int rc = SSL_write(m_ssl, buffer, static_cast (count)); + handleError(rc); + + if (rc < 0) + rc = 0; + + return rc; +} + + void TLSSocket_OpenSSL::handshake(ref toHandler) { if (toHandler) @@ -260,6 +278,13 @@ ref TLSSocket_OpenSSL::getPeerCertificates() } +void TLSSocket_OpenSSL::internalThrow() +{ + if (m_ex.get()) + throw *m_ex; +} + + void TLSSocket_OpenSSL::handleError(int rc) { if (rc > 0) return; @@ -277,12 +302,14 @@ void TLSSocket_OpenSSL::handleError(int rc) if (lastError == 0) { if (rc == 0) + { throw exceptions::tls_exception("SSL connection unexpectedly closed"); + } else { vmime::string msg; std::ostringstream oss(msg); - oss << "The BIO reported an error: %d" << rc; + oss << "The BIO reported an error: " << rc; oss.flush(); throw exceptions::tls_exception(oss.str()); } @@ -290,10 +317,16 @@ void TLSSocket_OpenSSL::handleError(int rc) break; } - //// Follwoing errors should not occur - // With SSL_MODE_AUTO_RETRY these should not happen case SSL_ERROR_WANT_READ: + + BIO_set_retry_read(SSL_get_rbio(m_ssl)); + break; + case SSL_ERROR_WANT_WRITE: + + BIO_set_retry_write(SSL_get_wbio(m_ssl)); + break; + // This happens only for BIOs of type BIO_s_connect() or BIO_s_accept() case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: @@ -319,6 +352,12 @@ void TLSSocket_OpenSSL::handleError(int rc) } +unsigned int TLSSocket_OpenSSL::getStatus() const +{ + return m_wrapped->getStatus(); +} + + // Implementation of custom BIO methods @@ -330,9 +369,28 @@ int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) TLSSocket_OpenSSL *sok = reinterpret_cast (bio->ptr); - sok->m_wrapped->sendRaw(buf, len); + try + { + BIO_clear_retry_flags(bio); - return len; + const size_type n = sok->m_wrapped->sendRawNonBlocking(buf, len); + + BIO_clear_retry_flags(bio); + + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) + { + BIO_set_retry_write(bio); + return -1; + } + + return static_cast (len); + } + catch (exception& e) + { + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } } @@ -344,21 +402,33 @@ int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) TLSSocket_OpenSSL *sok = reinterpret_cast (bio->ptr); - const int n = sok->m_wrapped->receiveRaw(buf, len); + try + { + const size_type n = sok->m_wrapped->receiveRaw(buf, len); - if (n == 0) - BIO_set_retry_read(bio); // This seems like a hack, really... - else BIO_clear_retry_flags(bio); - return n; + if (sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) + { + BIO_set_retry_read(bio); + return -1; + } + + return static_cast (n); + } + catch (exception& e) + { + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } } // static int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) { - return bio_write(bio, str, strlen(str)); + return bio_write(bio, str, static_cast (strlen(str))); } diff --git a/src/platforms/posix/posixSocket.cpp b/src/platforms/posix/posixSocket.cpp index 6fcbd118..b3e33f49 100644 --- a/src/platforms/posix/posixSocket.cpp +++ b/src/platforms/posix/posixSocket.cpp @@ -43,6 +43,13 @@ #include "vmime/exception.hpp" +#if defined(EWOULDBLOCK) +# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EWOULDBLOCK || (x) == EINTR) +#else +# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EINTR) +#endif + + namespace vmime { namespace platforms { namespace posix { @@ -53,7 +60,7 @@ namespace posix { // posixSocket::posixSocket(ref th) - : m_timeoutHandler(th), m_desc(-1) + : m_timeoutHandler(th), m_desc(-1), m_status(0) { } @@ -332,13 +339,15 @@ posixSocket::size_type posixSocket::getBlockSize() const void posixSocket::receive(vmime::string& buffer) { - const int size = receiveRaw(m_buffer, sizeof(m_buffer)); + const size_type size = receiveRaw(m_buffer, sizeof(m_buffer)); buffer = vmime::string(m_buffer, size); } posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type count) { + m_status &= ~STATUS_WOULDBLOCK; + // Check whether data is available fd_set fds; FD_ZERO(&fds); @@ -348,11 +357,11 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou tv.tv_sec = 1; tv.tv_usec = 0; - int ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv); + ssize_t ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv); if (ret < 0) { - if (errno != EAGAIN) + if (!IS_EAGAIN(errno)) throwSocketError(errno); // No data available at this time @@ -372,6 +381,8 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou } } + m_status |= STATUS_WOULDBLOCK; + // Continue waiting for data return 0; } @@ -381,9 +392,27 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou if (ret < 0) { - if (errno != EAGAIN) + if (!IS_EAGAIN(errno)) throwSocketError(errno); + // Check if we are timed out + if (m_timeoutHandler && + m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + { + // Server did not react within timeout delay + throwSocketError(errno); + } + else + { + // Reset timeout + m_timeoutHandler->resetTimeOut(); + } + } + + m_status |= STATUS_WOULDBLOCK; + // No data available at this time return 0; } @@ -411,15 +440,17 @@ void posixSocket::send(const vmime::string& buffer) void posixSocket::sendRaw(const char* buffer, const size_type count) { + m_status &= ~STATUS_WOULDBLOCK; + size_type size = count; while (size > 0) { - const int ret = ::send(m_desc, buffer, size, 0); + const ssize_t ret = ::send(m_desc, buffer, size, 0); if (ret < 0) { - if (errno != EAGAIN) + if (!IS_EAGAIN(errno)) throwSocketError(errno); platform::getHandler()->wait(); @@ -437,6 +468,27 @@ void posixSocket::sendRaw(const char* buffer, const size_type count) } +posixSocket::size_type posixSocket::sendRawNonBlocking(const char* buffer, const size_type count) +{ + m_status &= ~STATUS_WOULDBLOCK; + + const ssize_t ret = ::send(m_desc, buffer, count, 0); + + if (ret < 0) + { + if (!IS_EAGAIN(errno)) + throwSocketError(errno); + + m_status |= STATUS_WOULDBLOCK; + + // No data can be written at this time + return 0; + } + + return ret; +} + + void posixSocket::throwSocketError(const int err) { string msg; @@ -473,6 +525,12 @@ void posixSocket::throwSocketError(const int err) } +unsigned int posixSocket::getStatus() const +{ + return m_status; +} + + // // posixSocketFactory diff --git a/src/platforms/windows/windowsSocket.cpp b/src/platforms/windows/windowsSocket.cpp index 27cbcaa7..7f58e279 100644 --- a/src/platforms/windows/windowsSocket.cpp +++ b/src/platforms/windows/windowsSocket.cpp @@ -40,11 +40,11 @@ namespace windows { // -// posixSocket +// windowsSocket // windowsSocket::windowsSocket(ref th) - : m_timeoutHandler(th), m_desc(-1) + : m_timeoutHandler(th), m_desc(INVALID_SOCKET), m_status(0) { WSAData wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); @@ -53,8 +53,9 @@ windowsSocket::windowsSocket(ref th) windowsSocket::~windowsSocket() { - if (m_desc != -1) + if (m_desc != INVALID_SOCKET) ::closesocket(m_desc); + WSACleanup(); } @@ -62,10 +63,10 @@ windowsSocket::~windowsSocket() void windowsSocket::connect(const vmime::string& address, const vmime::port_t port) { // Close current connection, if any - if (m_desc != -1) + if (m_desc != INVALID_SOCKET) { ::closesocket(m_desc); - m_desc = -1; + m_desc = INVALID_SOCKET; } // Resolve address @@ -93,35 +94,64 @@ void windowsSocket::connect(const vmime::string& address, const vmime::port_t po // Get a new socket m_desc = ::socket(AF_INET, SOCK_STREAM, 0); - if (m_desc == -1) - throw vmime::exceptions::connection_error("Error while creating socket."); + if (m_desc == INVALID_SOCKET) + { + try + { + int err = WSAGetLastError(); + throwSocketError(err); + } + catch (exceptions::socket_exception& e) + { + throw vmime::exceptions::connection_error + ("Error while creating socket.", e); + } + } // Start connection if (::connect(m_desc, reinterpret_cast (&addr), sizeof(addr)) == -1) { - ::closesocket(m_desc); - m_desc = -1; + try + { + int err = WSAGetLastError(); + throwSocketError(err); + } + catch (exceptions::socket_exception& e) + { + ::closesocket(m_desc); + m_desc = INVALID_SOCKET; - // Error - throw vmime::exceptions::connection_error("Error while connecting socket."); + // Error + throw vmime::exceptions::connection_error + ("Error while connecting socket.", e); + } } + + // Set socket to non-blocking + unsigned long non_blocking = 1; + ::ioctlsocket(m_desc, FIONBIO, &non_blocking); } bool windowsSocket::isConnected() const { - return (m_desc != -1); + if (m_desc == INVALID_SOCKET) + return false; + + char buff; + + return ::recv(m_desc, &buff, 1, MSG_PEEK) != 0; } void windowsSocket::disconnect() { - if (m_desc != -1) + if (m_desc != INVALID_SOCKET) { ::shutdown(m_desc, SD_BOTH); ::closesocket(m_desc); - m_desc = -1; + m_desc = INVALID_SOCKET; } } @@ -134,48 +164,200 @@ windowsSocket::size_type windowsSocket::getBlockSize() const void windowsSocket::receive(vmime::string& buffer) { - int ret = ::recv(m_desc, m_buffer, sizeof(m_buffer), 0); - - if (ret == -1) - { - // Error or no data - return; - } - else if (ret > 0) - { - buffer = vmime::string(m_buffer, ret); - } + const size_type size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = vmime::string(m_buffer, size); } windowsSocket::size_type windowsSocket::receiveRaw(char* buffer, const size_type count) { + m_status &= ~STATUS_WOULDBLOCK; + + // Check whether data is available + bool timedout; + waitForData(READ, timedout); + + if (timedout) + { + // No data available at this time + // Check if we are timed out + if (m_timeoutHandler && + m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + { + // Server did not react within timeout delay + throwSocketError(WSAETIMEDOUT); + } + else + { + // Reset timeout + m_timeoutHandler->resetTimeOut(); + } + } + + // Continue waiting for data + return 0; + } + + // Read available data int ret = ::recv(m_desc, buffer, count, 0); - if (ret == -1) + if (ret == SOCKET_ERROR) { + int err = WSAGetLastError(); + + if (err != WSAEWOULDBLOCK) + throwSocketError(err); + + m_status |= STATUS_WOULDBLOCK; + // Error or no data return (0); } + else if (ret == 0) + { + // Host shutdown + throwSocketError(WSAENOTCONN); + } else { - return (ret); + // Data received, reset timeout + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + return ret; } } void windowsSocket::send(const vmime::string& buffer) { - ::send(m_desc, buffer.data(), buffer.length(), 0); + sendRaw(buffer.data(), buffer.length()); } void windowsSocket::sendRaw(const char* buffer, const size_type count) { - ::send(m_desc, buffer, count, 0); + m_status &= ~STATUS_WOULDBLOCK; + + size_type size = count; + + while (size > 0) + { + const int ret = ::send(m_desc, buffer, size, 0); + + if (ret == SOCKET_ERROR) + { + int err = WSAGetLastError(); + + if (err != WSAEWOULDBLOCK) + throwSocketError(err); + + bool timedout; + waitForData(WRITE, timedout); + } + else + { + buffer += ret; + size -= ret; + } + } + + // Reset timeout + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); } +windowsSocket::size_type windowsSocket::sendRawNonBlocking(const char* buffer, const size_type count, const bool block) +{ + m_status &= ~STATUS_WOULDBLOCK; + + const int ret = ::send(m_desc, buffer, count, 0); + + if (ret == SOCKET_ERROR) + { + int err = WSAGetLastError(); + + if (err == WSAEWOULDBLOCK) + { + m_status |= STATUS_WOULDBLOCK; + + // No data can be written at this time + return 0; + } + else + { + throwSocketError(err); + } + } + + return ret; +} + + +unsigned int windowsSocket::getStatus() const +{ + return m_status; +} + + +void windowsSocket::throwSocketError(const int err) +{ + std::ostringstream oss; + string msg; + + LPTSTR str; + + if (::FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) &str, 0, NULL) == 0) + { + // Failed getting message + oss << "Unknown socket error (code " << err << ")"; + } + else + { + oss << str; + ::LocalFree(str); + } + + msg = oss.str(); + + throw exceptions::socket_exception(msg); +} + + +void windowsSocket::waitForData(const WaitOpType t, bool& timedOut) +{ + // Check whether data is available + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_desc, &fds); + + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + + int ret; + + if (t & READ) + ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv); + else if (t & WRITE) + ret = ::select(m_desc + 1, NULL, &fds, NULL, &tv); + else + ret = ::select(m_desc + 1, &fds, &fds, NULL, &tv); + + timedOut = (ret == 0); + + if (ret == SOCKET_ERROR) + { + int err = WSAGetLastError(); + throwSocketError(err); + } +} + // diff --git a/src/security/sasl/SASLSocket.cpp b/src/security/sasl/SASLSocket.cpp index 28f5f6f6..531e4188 100644 --- a/src/security/sasl/SASLSocket.cpp +++ b/src/security/sasl/SASLSocket.cpp @@ -177,6 +177,40 @@ void SASLSocket::sendRaw(const char* buffer, const size_type count) } +SASLSocket::size_type SASLSocket::sendRawNonBlocking(const char* buffer, const size_type count) +{ + byte_t* output = 0; + int outputLen = 0; + + m_session->getMechanism()->encode + (m_session, reinterpret_cast (buffer), count, + &output, &outputLen); + + size_type bytesSent = 0; + + try + { + bytesSent = m_wrapped->sendRawNonBlocking + (reinterpret_cast (output), outputLen); + } + catch (...) + { + delete [] output; + throw; + } + + delete [] output; + + return bytesSent; +} + + +unsigned int SASLSocket::getStatus() const +{ + return m_wrapped->getStatus(); +} + + } // sasl } // security } // vmime diff --git a/vmime/net/socket.hpp b/vmime/net/socket.hpp index 0fac6514..4551e3e2 100644 --- a/vmime/net/socket.hpp +++ b/vmime/net/socket.hpp @@ -47,11 +47,17 @@ class socket : public object { public: + enum Status + { + STATUS_WOULDBLOCK = 0x1 /**< The receive operation would block. */ + }; + + virtual ~socket() { } /** Type used for lengths in streams. */ - typedef int size_type; + typedef long size_type; /** Connect to the specified address and port. @@ -84,7 +90,7 @@ public: * @param count maximum number of bytes to receive (size of buffer) * @return number of bytes received/written into output buffer */ - virtual int receiveRaw(char* buffer, const size_type count) = 0; + virtual size_type receiveRaw(char* buffer, const size_type count) = 0; /** Send (text) data to the socket. * @@ -99,6 +105,15 @@ public: */ virtual void sendRaw(const char* buffer, const size_type count) = 0; + /** Send (raw) data to the socket. + * Function may returns before all data is sent. + * + * @param buffer data to send + * @param count number of bytes to send (size of buffer) + * @return number of bytes sent + */ + virtual size_type sendRawNonBlocking(const char* buffer, const size_type count) = 0; + /** Return the preferred maximum block size when reading * from or writing to this stream. * @@ -106,6 +121,12 @@ public: */ virtual size_type getBlockSize() const = 0; + /** Return the current status of this socket. + * + * @return status flags for this socket + */ + virtual unsigned int getStatus() const = 0; + protected: socket() { } diff --git a/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp index 729923f3..ca113f17 100644 --- a/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp +++ b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -70,9 +70,12 @@ public: void send(const string& buffer); void sendRaw(const char* buffer, const size_type count); + size_type sendRawNonBlocking(const char* buffer, const size_type count); size_type getBlockSize() const; + unsigned int getStatus() const; + private: void internalThrow(); @@ -97,6 +100,8 @@ private: ref m_toHandler; exception* m_ex; + + unsigned int m_status; }; diff --git a/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp index 401fad12..ab4093f7 100755 --- a/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp +++ b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -36,6 +36,8 @@ #include "vmime/net/tls/TLSSocket.hpp" +#include + #include @@ -72,9 +74,12 @@ public: void send(const string& buffer); void sendRaw(const char* buffer, const size_type count); + size_type sendRawNonBlocking(const char* buffer, const size_type count); size_type getBlockSize() const; + unsigned int getStatus() const; + private: static int bio_write(BIO* bio, const char* buf, int len); @@ -87,8 +92,10 @@ private: void createSSLHandle(); + void internalThrow(); void handleError(int rc); + ref m_session; ref m_wrapped; @@ -100,6 +107,9 @@ private: ref m_toHandler; SSL* m_ssl; + + // Last exception thrown from C BIO functions + std::auto_ptr m_ex; }; diff --git a/vmime/platforms/posix/posixSocket.hpp b/vmime/platforms/posix/posixSocket.hpp index 1332505b..78b1c0aa 100644 --- a/vmime/platforms/posix/posixSocket.hpp +++ b/vmime/platforms/posix/posixSocket.hpp @@ -55,9 +55,12 @@ public: void send(const vmime::string& buffer); void sendRaw(const char* buffer, const size_type count); + size_type sendRawNonBlocking(const char* buffer, const size_type count); size_type getBlockSize() const; + unsigned int getStatus() const; + protected: static void throwSocketError(const int err); @@ -68,6 +71,8 @@ private: char m_buffer[65536]; int m_desc; + + unsigned int m_status; }; diff --git a/vmime/platforms/windows/windowsSocket.hpp b/vmime/platforms/windows/windowsSocket.hpp index c41d8bfe..ca007a06 100644 --- a/vmime/platforms/windows/windowsSocket.hpp +++ b/vmime/platforms/windows/windowsSocket.hpp @@ -43,6 +43,7 @@ namespace windows { class windowsSocket : public vmime::net::socket { public: + windowsSocket(); windowsSocket(ref th); ~windowsSocket(); @@ -58,15 +59,33 @@ public: void send(const vmime::string& buffer); void sendRaw(const char* buffer, const size_type count); + size_type sendRawNonBlocking(const char* buffer, const size_type count); size_type getBlockSize() const; + unsigned int getStatus() const; + +protected: + + void throwSocketError(const int err); + + enum WaitOpType + { + READ = 1, + WRITE = 2, + BOTH = 4 + }; + + void waitForData(const WaitOpType t, bool& timedOut); + private: ref m_timeoutHandler; char m_buffer[65536]; SOCKET m_desc; + + unsigned int m_status; }; diff --git a/vmime/security/sasl/SASLSocket.hpp b/vmime/security/sasl/SASLSocket.hpp index f05ed424..d6f4ecd9 100644 --- a/vmime/security/sasl/SASLSocket.hpp +++ b/vmime/security/sasl/SASLSocket.hpp @@ -63,9 +63,12 @@ public: void send(const string& buffer); void sendRaw(const char* buffer, const size_type count); + size_type sendRawNonBlocking(const char* buffer, const size_type count); size_type getBlockSize() const; + unsigned int getStatus() const; + private: ref m_session;