1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/net/quoted_printable.h"
6
7#include "base/logging.h"
8#include "base/string_util.h"
9
10namespace {
11
12const int kMaxCharPerLine = 76;
13const char* const kEOL = "\r\n";
14
15const char kHexTable[] = "0123456789ABCDEF";
16
17}  // namespace
18
19namespace chrome {
20namespace browser {
21namespace net {
22
23void QuotedPrintableEncode(const std::string& input, std::string* output) {
24  // The number of characters in the current line.
25  int char_count = 0;
26  for (std::string::const_iterator iter = input.begin();
27       iter != input.end(); ++iter) {
28    bool last_char = (iter + 1 == input.end());
29    char c = *iter;
30    // Whether this character can be inserted without encoding.
31    bool as_is = false;
32    // All printable ASCII characters can be included as is (but for =).
33    if (c >= '!' && c <= '~' && c != '=') {
34      as_is = true;
35    }
36
37    // Space and tab characters can be included as is if they don't appear at
38    // the end of a line or at then end of the input.
39    if (!as_is && (c == '\t' || c == ' ') && !last_char &&
40        !IsEOL(iter + 1, input)) {
41      as_is = true;
42    }
43
44    // End of line should be converted to CR-LF sequences.
45    if (!last_char) {
46      int eol_len = IsEOL(iter, input);
47      if (eol_len > 0) {
48        output->append(kEOL);
49        char_count = 0;
50        iter += (eol_len - 1);  // -1 because we'll ++ in the for() above.
51        continue;
52      }
53    }
54
55    // Insert a soft line break if necessary.
56    int min_chars_needed = as_is ? kMaxCharPerLine - 2 : kMaxCharPerLine - 4;
57    if (!last_char && char_count > min_chars_needed) {
58      output->append("=");
59      output->append(kEOL);
60      char_count = 0;
61    }
62
63    // Finally, insert the actual character(s).
64    if (as_is) {
65      output->append(1, c);
66      char_count++;
67    } else {
68      output->append("=");
69      output->append(1, kHexTable[static_cast<int>((c >> 4) & 0xF)]);
70      output->append(1, kHexTable[static_cast<int>(c & 0x0F)]);
71      char_count += 3;
72    }
73  }
74}
75
76bool QuotedPrintableDecode(const std::string& input, std::string* output) {
77  bool success = true;
78  for (std::string::const_iterator iter = input.begin();
79       iter!= input.end(); ++iter) {
80    char c = *iter;
81    if (c != '=') {
82      output->append(1, c);
83      continue;
84    }
85    if (input.end() - iter < 3) {
86      LOG(ERROR) << "unfinished = sequence in input string.";
87      success = false;
88      output->append(1, c);
89      continue;
90    }
91    char c2 = *(++iter);
92    char c3 = *(++iter);
93    if (c2 == '\r' && c3 == '\n') {
94      // Soft line break, ignored.
95      continue;
96    }
97
98    if (!IsHexDigit(c2) || !IsHexDigit(c3)) {
99      LOG(ERROR) << "invalid = sequence, = followed by non hexa digit " <<
100          "chars: " << c2 << " " << c3;
101      success = false;
102      // Just insert the chars as is.
103      output->append("=");
104      output->append(1, c2);
105      output->append(1, c3);
106      continue;
107    }
108
109    int i1 = HexDigitToInt(c2);
110    int i2 = HexDigitToInt(c3);
111    char r = static_cast<char>(((i1 << 4) & 0xF0) | (i2 & 0x0F));
112    output->append(1, r);
113  }
114  return success;
115}
116
117int IsEOL(const std::string::const_iterator& iter, const std::string& input) {
118  if (*iter == '\n')
119    return 1;  // Single LF.
120
121  if (*iter == '\r') {
122    if ((iter + 1) == input.end() || *(iter + 1) != '\n')
123      return 1;  // Single CR (Commodore and Old Macs).
124    return 2;  // CR-LF.
125  }
126
127  return 0;
128}
129
130}  // namespace net
131}  // namespace browser
132}  // namespace chrome
133