diff options
author | Vincent Richard <[email protected]> | 2012-12-06 10:02:31 +0000 |
---|---|---|
committer | Vincent Richard <[email protected]> | 2012-12-06 10:02:31 +0000 |
commit | 1ba5e8698cb0f3904fc7a1faf50597bc877b5099 (patch) | |
tree | 1b8b99a6bb147c7f6de31c4f242d7cf4aa240003 /src/net/pop3/POP3Store.cpp | |
parent | Fixed progression notification when message size is lower than 64K. (diff) | |
download | vmime-1ba5e8698cb0f3904fc7a1faf50597bc877b5099.tar.gz vmime-1ba5e8698cb0f3904fc7a1faf50597bc877b5099.zip |
Moved POP3 response receiving and parsing to a separate class.
Diffstat (limited to 'src/net/pop3/POP3Store.cpp')
-rw-r--r-- | src/net/pop3/POP3Store.cpp | 370 |
1 files changed, 34 insertions, 336 deletions
diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp index 749c5dea..a1dac0a0 100644 --- a/src/net/pop3/POP3Store.cpp +++ b/src/net/pop3/POP3Store.cpp @@ -29,14 +29,12 @@ #include "vmime/net/pop3/POP3Store.hpp" #include "vmime/net/pop3/POP3Folder.hpp" +#include "vmime/net/pop3/POP3Response.hpp" #include "vmime/exception.hpp" #include "vmime/platform.hpp" #include "vmime/messageId.hpp" #include "vmime/security/digest/messageDigestFactory.hpp" -#include "vmime/utility/filteredStream.hpp" -#include "vmime/utility/stringUtils.hpp" -#include "vmime/utility/inputStreamSocketAdapter.hpp" #include "vmime/net/defaultConnectionInfos.hpp" @@ -174,13 +172,12 @@ void POP3Store::connect() // eg: C: <connection to server> // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]> - string response; - readResponse(response, false); + ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler); - if (!isSuccessResponse(response)) + if (!response->isSuccess()) { internalDisconnect(); - throw exceptions::connection_greeting_error(response); + throw exceptions::connection_greeting_error(response->getFirstLine()); } #if VMIME_HAVE_TLS_SUPPORT @@ -217,7 +214,7 @@ void POP3Store::connect() #endif // VMIME_HAVE_TLS_SUPPORT // Start authentication process - authenticate(messageId(response)); + authenticate(messageId(response->getText())); } @@ -265,7 +262,7 @@ void POP3Store::authenticate(const messageId& randomMID) const string username = getAuthenticator()->getUsername(); const string password = getAuthenticator()->getPassword(); - string response; + ref <POP3Response> response; if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP)) { @@ -280,9 +277,9 @@ void POP3Store::authenticate(const messageId& randomMID) md5->finalize(); sendRequest("APOP " + username + " " + md5->getHexDigest()); - readResponse(response, false); + response = POP3Response::readResponse(m_socket, m_timeoutHandler); - if (isSuccessResponse(response)) + if (response->isSuccess()) { m_authentified = true; return; @@ -302,20 +299,19 @@ void POP3Store::authenticate(const messageId& randomMID) { // Can't fallback on basic authentication internalDisconnect(); - throw exceptions::authentication_error(response); + throw exceptions::authentication_error(response->getFirstLine()); } // Ensure connection is valid (cf. note above) try { - string response2; sendRequest("NOOP"); - readResponse(response2, false); + POP3Response::readResponse(m_socket, m_timeoutHandler); } catch (exceptions::socket_exception&) { internalDisconnect(); - throw exceptions::authentication_error(response); + throw exceptions::authentication_error(response->getFirstLine()); } } } @@ -339,21 +335,21 @@ void POP3Store::authenticate(const messageId& randomMID) // C: PASS couic // S: +OK vincent's maildrop has 2 messages (320 octets) sendRequest("USER " + username); - readResponse(response, false); + response = POP3Response::readResponse(m_socket, m_timeoutHandler); - if (!isSuccessResponse(response)) + if (!response->isSuccess()) { internalDisconnect(); - throw exceptions::authentication_error(response); + throw exceptions::authentication_error(response->getFirstLine()); } sendRequest("PASS " + password); - readResponse(response, false); + response = POP3Response::readResponse(m_socket, m_timeoutHandler); - if (!isSuccessResponse(response)) + if (!response->isSuccess()) { internalDisconnect(); - throw exceptions::authentication_error(response); + throw exceptions::authentication_error(response->getFirstLine()); } m_authentified = true; @@ -453,17 +449,16 @@ void POP3Store::authenticateSASL() for (bool cont = true ; cont ; ) { - string response; - readResponse(response, false); + ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler); - switch (getResponseCode(response)) + switch (response->getCode()) { - case RESPONSE_OK: + case POP3Response::CODE_OK: { m_socket = saslSession->getSecuredSocket(m_socket); return; } - case RESPONSE_READY: + case POP3Response::CODE_READY: { byte_t* challenge = 0; long challengeLen = 0; @@ -474,8 +469,7 @@ void POP3Store::authenticateSASL() try { // Extract challenge - stripResponseCode(response, response); - saslContext->decodeB64(response, &challenge, &challengeLen); + saslContext->decodeB64(response->getText(), &challenge, &challengeLen); // Prepare response saslSession->evaluateChallenge @@ -543,11 +537,10 @@ void POP3Store::startTLS() { sendRequest("STLS"); - string response; - readResponse(response, false); + ref <POP3Response> response = POP3Response::readResponse(m_socket, m_timeoutHandler); - if (getResponseCode(response) != RESPONSE_OK) - throw exceptions::command_error("STLS", response); + if (!response->isSuccess()) + throw exceptions::command_error("STLS", response->getFirstLine()); ref <tls::TLSSession> tlsSession = tls::TLSSession::create(getCertificateVerifier()); @@ -641,11 +634,11 @@ void POP3Store::noop() { sendRequest("NOOP"); - string response; - readResponse(response, false); + ref <POP3Response> response = + POP3Response::readResponse(m_socket, m_timeoutHandler); - if (!isSuccessResponse(response)) - throw exceptions::command_error("NOOP", response); + if (!response->isSuccess()) + throw exceptions::command_error("NOOP", response->getFirstLine()); } @@ -653,89 +646,21 @@ const std::vector <string> POP3Store::getCapabilities() { sendRequest("CAPA"); - string response; - readResponse(response, true); + ref <POP3Response> response = + POP3Response::readMultilineResponse(m_socket, m_timeoutHandler); std::vector <string> res; - if (isSuccessResponse(response)) + if (response->isSuccess()) { - stripFirstLine(response, response); - - std::istringstream iss(response); - string line; - - while (std::getline(iss, line, '\n')) - res.push_back(utility::stringUtils::trim(line)); + for (unsigned int i = 0, n = response->getLineCount() ; i < n ; ++i) + res.push_back(response->getLineAt(i)); } return res; } -bool POP3Store::isSuccessResponse(const string& buffer) -{ - return getResponseCode(buffer) == RESPONSE_OK; -} - - -bool POP3Store::stripFirstLine(const string& buffer, string& result, string* firstLine) -{ - const string::size_type end = buffer.find('\n'); - - if (end != string::npos) - { - if (firstLine) *firstLine = buffer.substr(0, end); - result = buffer.substr(end + 1); - return (true); - } - else - { - result = buffer; - return (false); - } -} - - -int POP3Store::getResponseCode(const string& buffer) -{ - if (buffer.length() >= 2) - { - // +[space] - if (buffer[0] == '+' && - (buffer[1] == ' ' || buffer[1] == '\t')) - { - return RESPONSE_READY; - } - - // +OK - if (buffer.length() >= 3) - { - if (buffer[0] == '+' && - (buffer[1] == 'O' || buffer[1] == 'o') && - (buffer[2] == 'K' || buffer[1] == 'k')) - { - return RESPONSE_OK; - } - } - } - - // -ERR or whatever - return RESPONSE_ERR; -} - - -void POP3Store::stripResponseCode(const string& buffer, string& result) -{ - const string::size_type pos = buffer.find_first_of(" \t"); - - if (pos != string::npos) - result = buffer.substr(pos + 1); - else - result = buffer; -} - - void POP3Store::sendRequest(const string& buffer, const bool end) { if (end) @@ -745,233 +670,6 @@ void POP3Store::sendRequest(const string& buffer, const bool end) } -void POP3Store::readResponse(string& buffer, const bool multiLine, - utility::progressListener* progress) -{ - bool foundTerminator = false; - long current = 0, total = 0; - - if (progress) - progress->start(total); - - if (m_timeoutHandler) - m_timeoutHandler->resetTimeOut(); - - buffer.clear(); - - string::value_type last1 = '\0', last2 = '\0'; - - for ( ; !foundTerminator ; ) - { -#if 0 // not supported - // Check for possible cancellation - if (progress && progress->cancel()) - throw exceptions::operation_cancelled(); -#endif - - // Check whether the time-out delay is elapsed - if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) - { - if (!m_timeoutHandler->handleTimeOut()) - throw exceptions::operation_timed_out(); - - m_timeoutHandler->resetTimeOut(); - } - - // Receive data from the socket - string receiveBuffer; - m_socket->receive(receiveBuffer); - - if (receiveBuffer.empty()) // buffer is empty - { - platform::getHandler()->wait(); - continue; - } - - // We have received data: reset the time-out counter - if (m_timeoutHandler) - m_timeoutHandler->resetTimeOut(); - - // Check for transparent characters: '\n..' becomes '\n.' - const string::value_type first = receiveBuffer[0]; - - if (first == '.' && last2 == '\n' && last1 == '.') - { - receiveBuffer.erase(receiveBuffer.begin()); - } - else if (receiveBuffer.length() >= 2 && first == '.' && - receiveBuffer[1] == '.' && last1 == '\n') - { - receiveBuffer.erase(receiveBuffer.begin()); - } - - for (string::size_type trans ; - string::npos != (trans = receiveBuffer.find("\n..")) ; ) - { - receiveBuffer.replace(trans, 3, "\n."); - } - - last1 = receiveBuffer[receiveBuffer.length() - 1]; - last2 = static_cast <char>((receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0); - - // Append the data to the response buffer - buffer += receiveBuffer; - current += receiveBuffer.length(); - - // Check for terminator string (and strip it if present) - foundTerminator = checkTerminator(buffer, multiLine); - - // Notify progress - if (progress) - { - total = std::max(total, current); - progress->progress(current, total); - } - - // If there is an error (-ERR) when executing a command that - // requires a multi-line response, the error response will - // include only one line, so we stop waiting for a multi-line - // terminator and check for a "normal" one. - if (multiLine && !foundTerminator && buffer.length() >= 4 && buffer[0] == '-') - { - foundTerminator = checkTerminator(buffer, false); - } - } - - if (progress) - progress->stop(total); -} - - -void POP3Store::readResponse(utility::outputStream& os, - utility::progressListener* progress, const int predictedSize) -{ - long current = 0, total = predictedSize; - - string temp; - bool codeDone = false; - - if (progress) - progress->start(total); - - if (m_timeoutHandler) - m_timeoutHandler->resetTimeOut(); - - utility::inputStreamSocketAdapter sis(*m_socket); - utility::stopSequenceFilteredInputStream <5> sfis1(sis, "\r\n.\r\n"); - utility::stopSequenceFilteredInputStream <3> sfis2(sfis1, "\n.\n"); - utility::dotFilteredInputStream dfis(sfis2); // "\n.." --> "\n." - - utility::inputStream& is = dfis; - - while (!is.eof()) - { -#if 0 // not supported - // Check for possible cancellation - if (progress && progress->cancel()) - throw exceptions::operation_cancelled(); -#endif - - // Check whether the time-out delay is elapsed - if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) - { - if (!m_timeoutHandler->handleTimeOut()) - throw exceptions::operation_timed_out(); - } - - // Receive data from the socket - utility::stream::value_type buffer[65536]; - const utility::stream::size_type read = is.read(buffer, sizeof(buffer)); - - if (read == 0) // buffer is empty - { - platform::getHandler()->wait(); - continue; - } - - // We have received data: reset the time-out counter - if (m_timeoutHandler) - m_timeoutHandler->resetTimeOut(); - - // Notify progress - current += read; - - if (progress) - { - total = std::max(total, current); - progress->progress(current, total); - } - - // If we don't have extracted the response code yet - if (!codeDone) - { - temp.append(buffer, read); - - string firstLine; - - if (stripFirstLine(temp, temp, &firstLine) == true) - { - if (!isSuccessResponse(firstLine)) - throw exceptions::command_error("?", firstLine); - - codeDone = true; - - os.write(temp.data(), temp.length()); - temp.clear(); - - continue; - } - } - else - { - // Inject the data into the output stream - os.write(buffer, read); - } - } - - if (progress) - progress->stop(total); -} - - -bool POP3Store::checkTerminator(string& buffer, const bool multiLine) -{ - // Multi-line response - if (multiLine) - { - static const string term1("\r\n.\r\n"); - static const string term2("\n.\n"); - - return (checkOneTerminator(buffer, term1) || - checkOneTerminator(buffer, term2)); - } - // Normal response - else - { - static const string term1("\r\n"); - static const string term2("\n"); - - return (checkOneTerminator(buffer, term1) || - checkOneTerminator(buffer, term2)); - } - - return (false); -} - - -bool POP3Store::checkOneTerminator(string& buffer, const string& term) -{ - if (buffer.length() >= term.length() && - std::equal(buffer.end() - term.length(), buffer.end(), term.begin())) - { - buffer.erase(buffer.end() - term.length(), buffer.end()); - return (true); - } - - return (false); -} - - void POP3Store::registerFolder(POP3Folder* folder) { m_folders.push_back(folder); |