// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/net/quoted_printable.h" #include "base/logging.h" #include "base/string_util.h" namespace { const int kMaxCharPerLine = 76; const char* const kEOL = "\r\n"; const char kHexTable[] = "0123456789ABCDEF"; } // namespace namespace chrome { namespace browser { namespace net { void QuotedPrintableEncode(const std::string& input, std::string* output) { // The number of characters in the current line. int char_count = 0; for (std::string::const_iterator iter = input.begin(); iter != input.end(); ++iter) { bool last_char = (iter + 1 == input.end()); char c = *iter; // Whether this character can be inserted without encoding. bool as_is = false; // All printable ASCII characters can be included as is (but for =). if (c >= '!' && c <= '~' && c != '=') { as_is = true; } // Space and tab characters can be included as is if they don't appear at // the end of a line or at then end of the input. if (!as_is && (c == '\t' || c == ' ') && !last_char && !IsEOL(iter + 1, input)) { as_is = true; } // End of line should be converted to CR-LF sequences. if (!last_char) { int eol_len = IsEOL(iter, input); if (eol_len > 0) { output->append(kEOL); char_count = 0; iter += (eol_len - 1); // -1 because we'll ++ in the for() above. continue; } } // Insert a soft line break if necessary. int min_chars_needed = as_is ? kMaxCharPerLine - 2 : kMaxCharPerLine - 4; if (!last_char && char_count > min_chars_needed) { output->append("="); output->append(kEOL); char_count = 0; } // Finally, insert the actual character(s). if (as_is) { output->append(1, c); char_count++; } else { output->append("="); output->append(1, kHexTable[static_cast((c >> 4) & 0xF)]); output->append(1, kHexTable[static_cast(c & 0x0F)]); char_count += 3; } } } bool QuotedPrintableDecode(const std::string& input, std::string* output) { bool success = true; for (std::string::const_iterator iter = input.begin(); iter!= input.end(); ++iter) { char c = *iter; if (c != '=') { output->append(1, c); continue; } if (input.end() - iter < 3) { LOG(ERROR) << "unfinished = sequence in input string."; success = false; output->append(1, c); continue; } char c2 = *(++iter); char c3 = *(++iter); if (c2 == '\r' && c3 == '\n') { // Soft line break, ignored. continue; } if (!IsHexDigit(c2) || !IsHexDigit(c3)) { LOG(ERROR) << "invalid = sequence, = followed by non hexa digit " << "chars: " << c2 << " " << c3; success = false; // Just insert the chars as is. output->append("="); output->append(1, c2); output->append(1, c3); continue; } int i1 = HexDigitToInt(c2); int i2 = HexDigitToInt(c3); char r = static_cast(((i1 << 4) & 0xF0) | (i2 & 0x0F)); output->append(1, r); } return success; } int IsEOL(const std::string::const_iterator& iter, const std::string& input) { if (*iter == '\n') return 1; // Single LF. if (*iter == '\r') { if ((iter + 1) == input.end() || *(iter + 1) != '\n') return 1; // Single CR (Commodore and Old Macs). return 2; // CR-LF. } return 0; } } // namespace net } // namespace browser } // namespace chrome