diff options
author | Vincent Richard <[email protected]> | 2008-10-12 13:59:09 +0000 |
---|---|---|
committer | Vincent Richard <[email protected]> | 2008-10-12 13:59:09 +0000 |
commit | 13f69779c298173bd21b83dd6cc538814f2b9155 (patch) | |
tree | 72c95e72fca7d0c916eea4faf39e7e636bfce8b6 /src/utility/encoder/qpEncoder.cpp | |
parent | Fixed compilation warnings. (diff) | |
download | vmime-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.cpp | 472 |
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 |