// // VMime library (http://vmime.sourceforge.net) // Copyright (C) 2002-2004 Vincent Richard // // 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 "encoderUUE.hpp" #include "parserHelpers.hpp" namespace vmime { encoderUUE::encoderUUE() { getProperties()["mode"] = 644; getProperties()["filename"] = "no_name"; getProperties()["maxlinelength"] = 46; } const std::vector encoderUUE::getAvailableProperties() const { std::vector list(encoder::getAvailableProperties()); list.push_back("maxlinelength"); list.push_back("mode"); list.push_back("filename"); return (list); } // This is the character encoding function to make a character printable static inline const unsigned char UUENCODE(const unsigned char c) { return ((c & 077) + ' '); } // Single character decoding static inline const unsigned char UUDECODE(const unsigned char c) { return ((c - ' ') & 077); } const utility::stream::size_type encoderUUE::encode(utility::inputStream& in, utility::outputStream& out) { in.reset(); // may not work... const string propFilename = getProperties().getProperty ("filename", ""); const string propMode = getProperties().getProperty ("mode", "644"); const string::size_type maxLineLength = std::min(getProperties().getProperty ("maxlinelength", 46), static_cast (46)); utility::stream::size_type total = 0; // Output the prelude text ("begin [mode] [filename]") out << "begin"; if (!propFilename.empty()) { out << " " << propMode << " " << propFilename; total += 2 + propMode.length() + propFilename.length(); } out << "\r\n"; total += 7; // Process the data utility::stream::value_type inBuffer[64]; utility::stream::value_type outBuffer[64]; while (!in.eof()) { // Process up to 45 characters per line std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); const utility::stream::size_type inLength = in.read(inBuffer, maxLineLength - 1); outBuffer[0] = UUENCODE(inLength); // Line length utility::stream::size_type j = 1; for (utility::stream::size_type i = 0 ; i < inLength ; i += 3, j += 4) { const unsigned char c1 = (unsigned char) inBuffer[i]; const unsigned char c2 = (unsigned char) inBuffer[i + 1]; const unsigned char c3 = (unsigned char) inBuffer[i + 2]; outBuffer[j] = UUENCODE(c1 >> 2); outBuffer[j + 1] = UUENCODE((c1 << 4) & 060 | (c2 >> 4) & 017); outBuffer[j + 2] = UUENCODE((c2 << 2) & 074 | (c3 >> 6) & 03); outBuffer[j + 3] = UUENCODE(c3 & 077); } outBuffer[j] = '\r'; outBuffer[j + 1] = '\n'; out.write(outBuffer, j + 2); total += j + 2; } out << "end\r\n"; total += 5; return (total); } const utility::stream::size_type encoderUUE::decode(utility::inputStream& in, utility::outputStream& out) { in.reset(); // may not work... // Process the data utility::stream::value_type inBuffer[64]; utility::stream::value_type outBuffer[64]; utility::stream::size_type total = 0; bool stop = false; std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); while (!stop && !in.eof()) { // Get the line length utility::stream::value_type lengthChar; if (in.read(&lengthChar, 1) == 0) break; const utility::stream::size_type outLength = UUDECODE(lengthChar); const utility::stream::size_type inLength = std::min((outLength * 4) / 3, (utility::stream::size_type) 64); utility::stream::value_type inPos = 0; switch (lengthChar) { case ' ': case '\t': case '\r': case '\n': { // Ignore continue; } case 'b': { // Read 5 characters more to check for begin ("begin ...\r\n" or "begin ...\n") inPos = in.read(inBuffer, 5); if (inPos == 5 && inBuffer[0] == 'e' && inBuffer[1] == 'g' && inBuffer[2] == 'i' && inBuffer[3] == 'n' && isspace(inBuffer[4])) { utility::stream::value_type c = 0; utility::stream::size_type count = 0; utility::stream::value_type buffer[512]; while (count < sizeof(buffer) - 1 && in.read(&c, 1) == 1) { if (c == '\n') break; buffer[count++] = c; } if (c != '\n') { // OOPS! Weird line. Don't try to decode more... return (total); } // Parse filename and mode if (count > 0) { buffer[count] = '\0'; utility::stream::value_type* p = buffer; while (*p && isspace(*p)) ++p; utility::stream::value_type* modeStart = buffer; while (*p && !isspace(*p)) ++p; getResults()["mode"] = string(modeStart, p); while (*p && isspace(*p)) ++p; utility::stream::value_type* filenameStart = buffer; while (*p && !(*p == '\r' || *p == '\n')) ++p; getResults()["filename"] = string(filenameStart, p); } // No filename or mode specified else { getResults()["filename"] = "untitled"; getResults()["mode"] = 644; } continue; } break; } case 'e': { // Read 3 characters more to check for end ("end\r\n" or "end\n") inPos = in.read(inBuffer, 3); if (inPos == 3 && inBuffer[0] == 'n' && inBuffer[1] == 'd' && (inBuffer[2] == '\r' || inBuffer[2] == '\n')) { stop = true; continue; } break; } } // Read encoded data if (in.read(inBuffer + inPos, inLength - inPos) != inLength - inPos) { // Premature end of data break; } // Decode data for (utility::stream::size_type i = 0, j = 0 ; i < inLength ; i += 4, j += 3) { const unsigned char c1 = (unsigned char) inBuffer[i]; const unsigned char c2 = (unsigned char) inBuffer[i + 1]; const unsigned char c3 = (unsigned char) inBuffer[i + 2]; const unsigned char c4 = (unsigned char) inBuffer[i + 3]; const utility::stream::size_type n = std::min(inLength - i, static_cast (3)); switch (n) { default: case 3: outBuffer[j + 2] = UUDECODE(c3) << 6 | UUDECODE(c4); case 2: outBuffer[j + 1] = UUDECODE(c2) << 4 | UUDECODE(c3) >> 2; case 1: outBuffer[j] = UUDECODE(c1) << 2 | UUDECODE(c2) >> 4; case 0: break; } total += n; } out.write(outBuffer, outLength); std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); } return (total); } } // vmime