// 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<int>((c >> 4) & 0xF)]);
output->append(1, kHexTable[static_cast<int>(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<char>(((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