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
10 namespace {
11
12 const int kMaxCharPerLine = 76;
13 const char* const kEOL = "\r\n";
14
15 const char kHexTable[] = "0123456789ABCDEF";
16
17 } // namespace
18
19 namespace chrome {
20 namespace browser {
21 namespace net {
22
QuotedPrintableEncode(const std::string & input,std::string * output)23 void 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
QuotedPrintableDecode(const std::string & input,std::string * output)76 bool 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
IsEOL(const std::string::const_iterator & iter,const std::string & input)117 int 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