aboutsummaryrefslogtreecommitdiffstats
path: root/src/encoderQP.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoderQP.cpp')
-rw-r--r--src/encoderQP.cpp422
1 files changed, 422 insertions, 0 deletions
diff --git a/src/encoderQP.cpp b/src/encoderQP.cpp
new file mode 100644
index 00000000..46125c93
--- /dev/null
+++ b/src/encoderQP.cpp
@@ -0,0 +1,422 @@
+//
+// VMime library (http://vmime.sourceforge.net)
+// Copyright (C) 2002-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#include "encoderQP.hpp"
+#include "parserHelpers.hpp"
+
+
+namespace vmime
+{
+
+
+encoderQP::encoderQP()
+{
+}
+
+
+const std::vector <string> encoderQP::availableProperties() const
+{
+ std::vector <string> list(encoder::availableProperties());
+
+ 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 encoderQP::sm_hexDigits[] = "0123456789ABCDEF";
+
+// Decoding table
+const unsigned char encoderQP::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
+};
+
+
+#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;
+
+
+const utility::stream::size_type encoderQP::encode(utility::inputStream& in, utility::outputStream& out)
+{
+ in.reset(); // may not work...
+
+ const string::size_type propMaxLineLength =
+ properties().get <string::size_type>("maxlinelength", (string::size_type) -1);
+
+ const bool rfc2047 = properties().get <bool>("rfc2047", false);
+ const bool text = properties().get <bool>("text", false); // binary mode by default
+
+ const bool cutLines = (propMaxLineLength != (string::size_type) -1);
+ const string::size_type maxLineLength = std::min(propMaxLineLength, (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;
+
+ while (bufferPos < bufferLength || !in.eof())
+ {
+ // Flush current output buffer
+ if (outBufferPos + 6 >= (int) sizeof(outBuffer))
+ {
+ out.write((char*) 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 = (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;
+ }
+ 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)
+ {
+ 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;
+ }
+ }
+
+ // Flush remaining output buffer
+ if (outBufferPos != 0)
+ {
+ out.write((char*) outBuffer, outBufferPos);
+ total += outBufferPos;
+ }
+
+ return (total);
+}
+
+
+const utility::stream::size_type encoderQP::decode(utility::inputStream& in, utility::outputStream& out)
+{
+ in.reset(); // may not work...
+
+ // Process the data
+ const bool rfc2047 = properties().get <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;
+
+ while (bufferPos < bufferLength || !in.eof())
+ {
+ // Flush current output buffer
+ if (outBufferPos >= (int) sizeof(outBuffer))
+ {
+ out.write((char*) 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 = (unsigned char) buffer[bufferPos++];
+
+ switch (c)
+ {
+ case '=':
+ {
+ if (bufferPos >= bufferLength)
+ {
+ bufferLength = in.read(buffer, sizeof(buffer));
+ bufferPos = 0;
+ }
+
+ if (bufferPos < bufferLength)
+ {
+ c = (unsigned char) buffer[bufferPos++];
+
+ 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;
+
+ 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 = (unsigned char) buffer[bufferPos++];
+
+ const unsigned char value =
+ 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;
+ }
+
+ }
+ }
+
+ // Flush remaining output buffer
+ if (outBufferPos != 0)
+ {
+ out.write((char*) outBuffer, outBufferPos);
+ total += outBufferPos;
+ }
+
+ return (total);
+}
+
+
+} // vmime