aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/utility/filteredStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/utility/filteredStream.cpp')
-rw-r--r--vmime-master/src/vmime/utility/filteredStream.cpp402
1 files changed, 402 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/utility/filteredStream.cpp b/vmime-master/src/vmime/utility/filteredStream.cpp
new file mode 100644
index 0000000..d5ec17b
--- /dev/null
+++ b/vmime-master/src/vmime/utility/filteredStream.cpp
@@ -0,0 +1,402 @@
+//
+// 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/utility/filteredStream.hpp"
+
+#include <algorithm>
+
+
+namespace vmime {
+namespace utility {
+
+
+// filteredInputStream
+
+size_t filteredInputStream::getBlockSize() {
+
+ return std::min(inputStream::getBlockSize(), getPreviousInputStream().getBlockSize());
+}
+
+
+// filteredOutputStream
+
+size_t filteredOutputStream::getBlockSize() {
+
+ return std::min(outputStream::getBlockSize(), getNextOutputStream().getBlockSize());
+}
+
+
+// dotFilteredInputStream
+
+dotFilteredInputStream::dotFilteredInputStream(inputStream& is)
+ : m_stream(is),
+ m_previousChar2('\0'),
+ m_previousChar1('\0') {
+
+}
+
+
+inputStream& dotFilteredInputStream::getPreviousInputStream() {
+
+ return m_stream;
+}
+
+
+bool dotFilteredInputStream::eof() const {
+
+ return m_stream.eof();
+}
+
+
+void dotFilteredInputStream::reset() {
+
+ m_previousChar2 = '\0';
+ m_previousChar1 = '\0';
+
+ m_stream.reset();
+}
+
+
+size_t dotFilteredInputStream::read(byte_t* const data, const size_t count) {
+
+ const size_t read = m_stream.read(data, count);
+
+ const byte_t* readPtr = data;
+ byte_t* writePtr = data;
+
+ const byte_t* end = data + read;
+
+ size_t written = 0;
+
+ byte_t prevChar2 = m_previousChar2;
+ byte_t prevChar1 = m_previousChar1;
+
+ // Replace "\n.." with "\n."
+ while (readPtr < end) {
+
+ if (*readPtr == '.' && prevChar2 == '\n' && prevChar1 == '.') {
+
+ // Ignore last dot
+ prevChar2 = '\0';
+ prevChar1 = '.';
+
+ } else {
+
+ *writePtr = *readPtr;
+
+ ++writePtr;
+ ++written;
+
+ prevChar2 = prevChar1;
+ prevChar1 = *readPtr;
+ }
+
+ ++readPtr;
+ }
+
+ m_previousChar2 = prevChar2;
+ m_previousChar1 = prevChar1;
+
+ return written;
+}
+
+
+size_t dotFilteredInputStream::skip(const size_t /* count */) {
+
+ // Skipping bytes is not supported
+ return 0;
+}
+
+
+// dotFilteredOutputStream
+
+dotFilteredOutputStream::dotFilteredOutputStream(outputStream& os)
+ : m_stream(os),
+ m_previousChar('\0'),
+ m_start(true) {
+
+}
+
+
+outputStream& dotFilteredOutputStream::getNextOutputStream() {
+
+ return m_stream;
+}
+
+
+void dotFilteredOutputStream::writeImpl(const byte_t* const data, const size_t count) {
+
+ if (count == 0) {
+ return;
+ }
+
+ const byte_t* pos = data;
+ const byte_t* end = data + count;
+ const byte_t* start = data;
+
+ if (m_previousChar == '.') {
+
+ if (data[0] == '\n' || data[0] == '\r') {
+
+ m_stream.write(".", 1); // extra <DOT>
+ m_stream.write(data, 1);
+
+ pos = data + 1;
+ }
+ }
+
+ // Replace "\n." with "\n.."
+ while ((pos = std::find(pos, end, '.')) != end) {
+
+ const byte_t previousChar = (pos == data ? m_previousChar : *(pos - 1));
+
+ if (previousChar == '\n') {
+
+ m_stream.write(start, pos - start);
+ m_stream.write("..", 2);
+
+ start = pos + 1;
+
+ } else if (pos == data && m_start) { // <DOT><CR><LF> at the beginning of content
+
+ m_stream.write(start, pos - start);
+
+ if (pos + 1 < end && (*(pos + 1) == '\n' || *(pos + 1) == '\r')) {
+ m_stream.write("..", 2);
+ } else {
+ m_stream.write(".", 1);
+ }
+
+ start = pos + 1;
+ }
+
+ ++pos;
+ }
+
+ m_stream.write(start, end - start);
+ m_previousChar = data[count - 1];
+ m_start = false;
+}
+
+
+void dotFilteredOutputStream::flush() {
+
+ // Do nothing
+ m_stream.flush();
+}
+
+
+size_t dotFilteredOutputStream::getBlockSize() {
+
+ return m_stream.getBlockSize();
+}
+
+
+// CRLFToLFFilteredOutputStream
+
+CRLFToLFFilteredOutputStream::CRLFToLFFilteredOutputStream(outputStream& os)
+ : m_stream(os),
+ m_previousChar('\0') {
+
+}
+
+
+outputStream& CRLFToLFFilteredOutputStream::getNextOutputStream() {
+
+ return m_stream;
+}
+
+
+void CRLFToLFFilteredOutputStream::writeImpl(const byte_t* const data, const size_t count) {
+
+ if (count == 0) {
+ return;
+ }
+
+ const byte_t* pos = data;
+ const byte_t* end = data + count;
+ const byte_t* start = data;
+
+ // Warning: if the whole buffer finishes with '\r', this
+ // last character will not be written back if flush() is
+ // not called
+ if (m_previousChar == '\r') {
+
+ if (*pos != '\n') {
+ m_stream.write("\r", 1); // write back \r
+ m_previousChar = *pos;
+ }
+ }
+
+ // Replace "\r\n" (CRLF) with "\n" (LF)
+ while ((pos = std::find(pos, end, '\n')) != end) {
+
+ const byte_t previousChar = (pos == data ? m_previousChar : *(pos - 1));
+
+ if (previousChar == '\r') {
+
+ if (pos != start) {
+ m_stream.write(start, pos - 1 - start); // do not write \r
+ }
+
+ m_stream.write("\n", 1);
+
+ start = pos + 1;
+ }
+
+ ++pos;
+ }
+
+ if (data[count - 1] == '\r') {
+
+ m_stream.write(start, end - start - 1);
+ m_previousChar = '\r';
+
+ } else {
+
+ m_stream.write(start, end - start);
+ m_previousChar = data[count - 1];
+ }
+}
+
+
+void CRLFToLFFilteredOutputStream::flush() {
+
+ m_stream.flush();
+
+ // TODO
+}
+
+
+size_t CRLFToLFFilteredOutputStream::getBlockSize() {
+
+ return m_stream.getBlockSize();
+}
+
+
+// LFToCRLFFilteredOutputStream
+
+LFToCRLFFilteredOutputStream::LFToCRLFFilteredOutputStream(outputStream& os)
+ : m_stream(os),
+ m_previousChar('\0') {
+
+}
+
+
+outputStream& LFToCRLFFilteredOutputStream::getNextOutputStream() {
+
+ return m_stream;
+}
+
+
+void LFToCRLFFilteredOutputStream::writeImpl(const byte_t* const data, const size_t count)
+{
+ if (count == 0) {
+ return;
+ }
+
+ string buffer;
+ buffer.reserve(count);
+
+ const byte_t* pos = data;
+ const byte_t* end = data + count;
+
+ byte_t previousChar = m_previousChar;
+
+ while (pos < end) {
+
+ switch (*pos) {
+
+ case '\r':
+
+ buffer.append(1, '\r');
+ buffer.append(1, '\n');
+
+ break;
+
+ case '\n':
+
+ if (previousChar != '\r') {
+ buffer.append(1, '\r');
+ buffer.append(1, '\n');
+ }
+
+ break;
+
+ default:
+
+ buffer.append(1, *pos);
+ break;
+ }
+
+ previousChar = *pos;
+ ++pos;
+ }
+
+ m_stream.write(&buffer[0], buffer.length());
+
+ m_previousChar = previousChar;
+}
+
+
+void LFToCRLFFilteredOutputStream::flush() {
+
+ m_stream.flush();
+}
+
+
+size_t LFToCRLFFilteredOutputStream::getBlockSize() {
+
+ return m_stream.getBlockSize();
+}
+
+
+// stopSequenceFilteredInputStream <1>
+
+template <>
+size_t stopSequenceFilteredInputStream <1>::read(byte_t* const data, const size_t count) {
+
+ if (eof() || m_stream.eof()) {
+ m_eof = true;
+ return 0;
+ }
+
+ const size_t read = m_stream.read(data, count);
+ byte_t* end = data + read;
+
+ byte_t* pos = std::find(data, end, m_sequence[0]);
+
+ if (pos == end) {
+
+ return read;
+
+ } else {
+
+ m_found = 1;
+ return pos - data;
+ }
+}
+
+
+} // utility
+} // vmime