diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/pop3/POP3Response.cpp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/pop3/POP3Response.cpp')
-rw-r--r-- | vmime-master/src/vmime/net/pop3/POP3Response.cpp | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/pop3/POP3Response.cpp b/vmime-master/src/vmime/net/pop3/POP3Response.cpp new file mode 100644 index 0000000..725841e --- /dev/null +++ b/vmime-master/src/vmime/net/pop3/POP3Response.cpp @@ -0,0 +1,504 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// 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_POP3 + + +#include "vmime/net/pop3/POP3Response.hpp" +#include "vmime/net/pop3/POP3Connection.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/inputStreamSocketAdapter.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3Response::POP3Response( + const shared_ptr <socket>& sok, + const shared_ptr <timeoutHandler>& toh, + const shared_ptr <tracer>& tracer +) + : m_socket(sok), + m_timeoutHandler(toh), + m_tracer(tracer) { + +} + + +// static +shared_ptr <POP3Response> POP3Response::readResponse( + const shared_ptr <POP3Connection>& conn +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string buffer; + resp->readResponseImpl(buffer, /* multiLine */ false); + + resp->m_firstLine = buffer; + resp->m_code = getResponseCode(buffer); + stripResponseCode(buffer, resp->m_text); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(buffer); + } + + return resp; +} + + +// static +shared_ptr <POP3Response> POP3Response::readMultilineResponse( + const shared_ptr <POP3Connection>& conn +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string buffer; + resp->readResponseImpl(buffer, /* multiLine */ true); + + string firstLine, nextLines; + stripFirstLine(buffer, nextLines, &firstLine); + + resp->m_firstLine = firstLine; + resp->m_code = getResponseCode(firstLine); + stripResponseCode(firstLine, resp->m_text); + + std::istringstream iss(nextLines); + string line; + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(firstLine); + } + + while (std::getline(iss, line, '\n')) { + + line = utility::stringUtils::trim(line); + resp->m_lines.push_back(line); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(line); + } + } + + if (resp->m_tracer) { + resp->m_tracer->traceReceive("."); + } + + return resp; +} + + +// static +shared_ptr <POP3Response> POP3Response::readLargeResponse( + const shared_ptr <POP3Connection>& conn, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize +) { + + shared_ptr <POP3Response> resp = shared_ptr <POP3Response>( + new POP3Response(conn->getSocket(), conn->getTimeoutHandler(), conn->getTracer()) + ); + + string firstLine; + const size_t length = resp->readResponseImpl(firstLine, os, progress, predictedSize); + + resp->m_firstLine = firstLine; + resp->m_code = getResponseCode(firstLine); + stripResponseCode(firstLine, resp->m_text); + + if (resp->m_tracer) { + resp->m_tracer->traceReceive(firstLine); + resp->m_tracer->traceReceiveBytes(length - firstLine.length()); + resp->m_tracer->traceReceive("."); + } + + return resp; +} + + +bool POP3Response::isSuccess() const { + + return m_code == CODE_OK; +} + + +const string POP3Response::getFirstLine() const { + + return m_firstLine; +} + + +POP3Response::ResponseCode POP3Response::getCode() const { + + return m_code; +} + + +const string POP3Response::getText() const { + + return m_text; +} + + +const string POP3Response::getLineAt(const size_t pos) const { + + return m_lines[pos]; +} + + +size_t POP3Response::getLineCount() const { + + return m_lines.size(); +} + + +void POP3Response::readResponseImpl(string& buffer, const bool multiLine) { + + bool foundTerminator = false; + + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + buffer.clear(); + + char last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) { + + // 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 + + if (m_socket->getStatus() & socket::STATUS_WANT_WRITE) { + m_socket->waitForWrite(); + } else { + m_socket->waitForRead(); + } + + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + // Check for transparent characters: '\n..' becomes '\n.' + const char 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 (size_t 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; + + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(buffer, multiLine); + + // 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); + } + } +} + + +size_t POP3Response::readResponseImpl( + string& firstLine, + utility::outputStream& os, + utility::progressListener* progress, + const size_t predictedSize +) { + + size_t 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()) { + + // 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 + byte_t buffer[65536]; + const size_t read = is.read(buffer, sizeof(buffer)); + + if (read == 0) { // buffer is empty + + if (m_socket->getStatus() & socket::STATUS_WANT_WRITE) { + m_socket->waitForWrite(); + } else if (m_socket->getStatus() & socket::STATUS_WANT_READ) { + m_socket->waitForRead(); + } else { + // Input stream needs more bytes to continue, but there + // is enough data into socket buffer. Do not waitForRead(), + // just retry read()ing on the stream. + } + + 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) { + + vmime::utility::stringUtils::appendBytesToString(temp, buffer, read); + + string responseData; + + if (stripFirstLine(temp, responseData, &firstLine) == true) { + + if (getResponseCode(firstLine) != CODE_OK) { + throw exceptions::command_error("?", firstLine); + } + + codeDone = true; + + os.write(responseData.data(), responseData.length()); + temp.clear(); + + continue; + } + + } else { + + // Inject the data into the output stream + os.write(buffer, read); + } + } + + if (progress) { + progress->stop(total); + } + + return current; +} + + +// static +bool POP3Response::stripFirstLine( + const string& buffer, + string& result, + string* firstLine +) { + + const size_t end = buffer.find('\n'); + + if (end != string::npos) { + + if (firstLine) { + *firstLine = utility::stringUtils::trim(buffer.substr(0, end)); + } + + result = buffer.substr(end + 1); + + return true; + + } else { + + if (firstLine) { + *firstLine = utility::stringUtils::trim(buffer); + } + + result = ""; + + return false; + } +} + + +// static +POP3Response::ResponseCode POP3Response::getResponseCode(const string& buffer) { + + if (buffer.length() >= 2) { + + // +[space] + if (buffer[0] == '+' && + (buffer[1] == ' ' || buffer[1] == '\t')) { + + return CODE_READY; + } + + // +OK + if (buffer.length() >= 3) { + + if (buffer[0] == '+' && + (buffer[1] == 'O' || buffer[1] == 'o') && + (buffer[2] == 'K' || buffer[1] == 'k')) { + + return CODE_OK; + } + } + } + + // -ERR or whatever + return CODE_ERR; +} + + +// static +void POP3Response::stripResponseCode(const string& buffer, string& result) { + + const size_t pos = buffer.find_first_of(" \t"); + + if (pos != string::npos) { + result = buffer.substr(pos + 1); + } else { + result = buffer; + } +} + + +// static +bool POP3Response::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; +} + + +// static +bool POP3Response::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; +} + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_POP3 |