aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/net/pop3/POP3Response.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/net/pop3/POP3Response.cpp')
-rw-r--r--vmime-master/src/vmime/net/pop3/POP3Response.cpp504
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