diff options
Diffstat (limited to 'src/encoderQP.cpp')
-rw-r--r-- | src/encoderQP.cpp | 422 |
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 |