aboutsummaryrefslogtreecommitdiffstats
path: root/src/utility/encoder/qpEncoder.cpp
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2008-10-12 13:59:09 +0000
committerVincent Richard <[email protected]>2008-10-12 13:59:09 +0000
commit13f69779c298173bd21b83dd6cc538814f2b9155 (patch)
tree72c95e72fca7d0c916eea4faf39e7e636bfce8b6 /src/utility/encoder/qpEncoder.cpp
parentFixed compilation warnings. (diff)
downloadvmime-13f69779c298173bd21b83dd6cc538814f2b9155.tar.gz
vmime-13f69779c298173bd21b83dd6cc538814f2b9155.zip
New namespace for encoders.
Diffstat (limited to 'src/utility/encoder/qpEncoder.cpp')
-rw-r--r--src/utility/encoder/qpEncoder.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/src/utility/encoder/qpEncoder.cpp b/src/utility/encoder/qpEncoder.cpp
new file mode 100644
index 00000000..737d488a
--- /dev/null
+++ b/src/utility/encoder/qpEncoder.cpp
@@ -0,0 +1,472 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2008 Vincent Richard <[email protected]>
+//
+// 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 2 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/encoder/qpEncoder.hpp"
+#include "vmime/parserHelpers.hpp"
+
+
+namespace vmime {
+namespace utility {
+namespace encoder {
+
+
+qpEncoder::qpEncoder()
+{
+}
+
+
+const std::vector <string> qpEncoder::getAvailableProperties() const
+{
+ std::vector <string> list(encoder::getAvailableProperties());
+
+ list.push_back("maxlinelength");
+
+ list.push_back("text"); // if set, '\r' and '\n' are not hex-encoded.
+ // WARNING! You should not use this for binary data!
+
+ list.push_back("rfc2047"); // for header fields encoding (RFC #2047)
+
+ return (list);
+}
+
+
+
+// Encoding table
+const unsigned char qpEncoder::sm_hexDigits[] = "0123456789ABCDEF";
+
+// Decoding table
+const unsigned char qpEncoder::sm_hexDecodeTable[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+#ifndef VMIME_BUILDING_DOC
+
+#define QP_ENCODE_HEX(x) \
+ outBuffer[outBufferPos] = '='; \
+ outBuffer[outBufferPos + 1] = sm_hexDigits[x >> 4]; \
+ outBuffer[outBufferPos + 2] = sm_hexDigits[x & 0xF]; \
+ outBufferPos += 3; \
+ curCol += 3;
+
+#define QP_WRITE(s, x, l) s.write(reinterpret_cast <utility::stream::value_type*>(x), l)
+
+#endif // VMIME_BUILDING_DOC
+
+
+utility::stream::size_type qpEncoder::encode(utility::inputStream& in,
+ utility::outputStream& out, utility::progressListener* progress)
+{
+ in.reset(); // may not work...
+
+ const string::size_type propMaxLineLength =
+ getProperties().getProperty <string::size_type>("maxlinelength", static_cast <string::size_type>(-1));
+
+ const bool rfc2047 = getProperties().getProperty <bool>("rfc2047", false);
+ const bool text = getProperties().getProperty <bool>("text", false); // binary mode by default
+
+ const bool cutLines = (propMaxLineLength != static_cast <string::size_type>(-1));
+ const string::size_type maxLineLength = std::min(propMaxLineLength, static_cast <string::size_type>(74));
+
+ // Process the data
+ char buffer[16384];
+ int bufferLength = 0;
+ int bufferPos = 0;
+
+ string::size_type curCol = 0;
+
+ unsigned char outBuffer[16384];
+ int outBufferPos = 0;
+
+ utility::stream::size_type total = 0;
+ utility::stream::size_type inTotal = 0;
+
+ if (progress)
+ progress->start(0);
+
+ while (bufferPos < bufferLength || !in.eof())
+ {
+ // Flush current output buffer
+ if (outBufferPos + 6 >= static_cast <int>(sizeof(outBuffer)))
+ {
+ QP_WRITE(out, outBuffer, outBufferPos);
+
+ total += outBufferPos;
+ outBufferPos = 0;
+ }
+
+ // Need to get more data?
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+
+ // No more data
+ if (bufferLength == 0)
+ break;
+ }
+
+ // Get the next char and encode it
+ const unsigned char c = static_cast <unsigned char>(buffer[bufferPos++]);
+
+ switch (c)
+ {
+ case '.':
+ {
+ if (!rfc2047 && curCol == 0)
+ {
+ // If a '.' appears at the beginning of a line, we encode it to
+ // to avoid problems with SMTP servers... ("\r\n.\r\n" means the
+ // end of data transmission).
+ QP_ENCODE_HEX('.')
+ continue;
+ }
+
+ outBuffer[outBufferPos++] = '.';
+ ++curCol;
+ break;
+ }
+ case ' ':
+ {
+ // RFC-2047, Page 5, 4.2. The "Q" encoding:
+ // << The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
+ // represented as "_" (underscore, ASCII 95.). >>
+ if (rfc2047)
+ {
+ outBuffer[outBufferPos++] = '_';
+ ++curCol;
+ }
+ else
+ {
+ // Need to get more data?
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+ }
+
+ // Spaces cannot appear at the end of a line. So, encode the space.
+ if (bufferPos >= bufferLength ||
+ (buffer[bufferPos] == '\r' || buffer[bufferPos] == '\n'))
+ {
+ QP_ENCODE_HEX(' ');
+ }
+ else
+ {
+ outBuffer[outBufferPos++] = ' ';
+ ++curCol;
+ }
+ }
+
+ break;
+ }
+ case '\t':
+ {
+ QP_ENCODE_HEX(c)
+ break;
+ }
+ case '\r':
+ case '\n':
+ {
+ // Text mode (where using CRLF or LF or ... does not
+ // care for a new line...)
+ if (text)
+ {
+ outBuffer[outBufferPos++] = c;
+ ++curCol;
+ }
+ // Binary mode (where CR and LF bytes are important!)
+ else
+ {
+ QP_ENCODE_HEX(c)
+ }
+
+ break;
+ }
+ case '=':
+ {
+ QP_ENCODE_HEX('=')
+ break;
+ }
+ // RFC-2047 'especials' characters
+ case ',':
+ case ';':
+ case ':':
+ case '_':
+ case '@':
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ {
+ if (rfc2047)
+ {
+ QP_ENCODE_HEX(c)
+ }
+ else
+ {
+ outBuffer[outBufferPos++] = c;
+ ++curCol;
+ }
+
+ break;
+ }
+ /*
+ Rule #2: (Literal representation) Octets with decimal values of 33
+ through 60 inclusive, and 62 through 126, inclusive, MAY be
+ represented as the ASCII characters which correspond to those
+ octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN
+ through TILDE, respectively).
+ */
+ default:
+ {
+ //if ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))
+ if (c >= 33 && c <= 126 && c != 61 && c != 63)
+ {
+ outBuffer[outBufferPos++] = c;
+ ++curCol;
+ }
+ // Other characters: '=' + hexadecimal encoding
+ else
+ {
+ QP_ENCODE_HEX(c)
+ }
+
+ break;
+ }
+
+ }
+
+ // Soft line break : "=\r\n"
+ if (cutLines && curCol >= maxLineLength - 1)
+ {
+ outBuffer[outBufferPos] = '=';
+ outBuffer[outBufferPos + 1] = '\r';
+ outBuffer[outBufferPos + 2] = '\n';
+
+ outBufferPos += 3;
+ curCol = 0;
+ }
+
+ ++inTotal;
+
+ if (progress)
+ progress->progress(inTotal, inTotal);
+ }
+
+ // Flush remaining output buffer
+ if (outBufferPos != 0)
+ {
+ QP_WRITE(out, outBuffer, outBufferPos);
+ total += outBufferPos;
+ }
+
+ if (progress)
+ progress->stop(inTotal);
+
+ return (total);
+}
+
+
+utility::stream::size_type qpEncoder::decode(utility::inputStream& in,
+ utility::outputStream& out, utility::progressListener* progress)
+{
+ in.reset(); // may not work...
+
+ // Process the data
+ const bool rfc2047 = getProperties().getProperty <bool>("rfc2047", false);
+
+ char buffer[16384];
+ int bufferLength = 0;
+ int bufferPos = 0;
+
+ unsigned char outBuffer[16384];
+ int outBufferPos = 0;
+
+ utility::stream::size_type total = 0;
+ utility::stream::size_type inTotal = 0;
+
+ while (bufferPos < bufferLength || !in.eof())
+ {
+ // Flush current output buffer
+ if (outBufferPos >= static_cast <int>(sizeof(outBuffer)))
+ {
+ QP_WRITE(out, outBuffer, outBufferPos);
+
+ total += outBufferPos;
+ outBufferPos = 0;
+ }
+
+ // Need to get more data?
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+
+ // No more data
+ if (bufferLength == 0)
+ break;
+ }
+
+ // Decode the next sequence (hex-encoded byte or printable character)
+ unsigned char c = static_cast <unsigned char>(buffer[bufferPos++]);
+
+ ++inTotal;
+
+ switch (c)
+ {
+ case '=':
+ {
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+ }
+
+ if (bufferPos < bufferLength)
+ {
+ c = static_cast <unsigned char>(buffer[bufferPos++]);
+
+ ++inTotal;
+
+ switch (c)
+ {
+ // Ignore soft line break ("=\r\n" or "=\n")
+ case '\r':
+
+ // Read one byte more
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+ }
+
+ if (bufferPos < bufferLength)
+ {
+ ++bufferPos;
+ ++inTotal;
+ }
+
+ break;
+
+ case '\n':
+
+ break;
+
+ // Hex-encoded char
+ default:
+ {
+ // We need another byte...
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+ }
+
+ if (bufferPos < bufferLength)
+ {
+ const unsigned char next = static_cast <unsigned char>(buffer[bufferPos++]);
+
+ ++inTotal;
+
+ const unsigned char value = static_cast <unsigned char>
+ (sm_hexDecodeTable[c] * 16 + sm_hexDecodeTable[next]);
+
+ outBuffer[outBufferPos++] = value;
+ }
+ else
+ {
+ // Premature end-of-data
+ }
+
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ // Premature end-of-data
+ }
+
+ break;
+ }
+ case '_':
+ {
+ if (rfc2047)
+ {
+ // RFC-2047, Page 5, 4.2. The "Q" encoding:
+ // << Note that the "_" always represents hexadecimal 20, even if the SPACE
+ // character occupies a different code position in the character set in use. >>
+ outBuffer[outBufferPos++] = 0x20;
+ break;
+ }
+
+ // no break here...
+ }
+ default:
+ {
+ outBuffer[outBufferPos++] = c;
+ }
+
+ }
+
+ if (progress)
+ progress->progress(inTotal, inTotal);
+ }
+
+ // Flush remaining output buffer
+ if (outBufferPos != 0)
+ {
+ QP_WRITE(out, outBuffer, outBufferPos);
+ total += outBufferPos;
+ }
+
+ if (progress)
+ progress->stop(inTotal);
+
+ return (total);
+}
+
+
+} // encoder
+} // utility
+} // vmime