1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "QuotedPrintable.h"
33
34#include <wtf/ASCIICType.h>
35#include <wtf/Vector.h>
36
37namespace WebCore {
38
39static const size_t maximumLineLength = 76;
40
41static const char crlfLineEnding[] = "\r\n";
42
43static size_t lengthOfLineEndingAtIndex(const char* input, size_t inputLength, size_t index)
44{
45 ASSERT_WITH_SECURITY_IMPLICATION(index < inputLength);
46 if (input[index] == '\n')
47 return 1; // Single LF.
48
49 if (input[index] == '\r') {
50 if ((index + 1) == inputLength || input[index + 1] != '\n')
51 return 1; // Single CR (Classic Mac OS).
52 return 2; // CR-LF.
53 }
54
55 return 0;
56}
57
58void quotedPrintableEncode(const Vector<char>& in, Vector<char>& out)
59{
60 quotedPrintableEncode(in.data(), in.size(), out);
61}
62
63void quotedPrintableEncode(const char* input, size_t inputLength, Vector<char>& out)
64{
65 out.clear();
66 out.reserveCapacity(inputLength);
67 size_t currentLineLength = 0;
68 for (size_t i = 0; i < inputLength; ++i) {
69 bool isLastCharacter = (i == inputLength - 1);
70 char currentCharacter = input[i];
71 bool requiresEncoding = false;
72 // All non-printable ASCII characters and = require encoding.
73 if ((currentCharacter < ' ' || currentCharacter > '~' || currentCharacter == '=') && currentCharacter != '\t')
74 requiresEncoding = true;
75
76 // Space and tab characters have to be encoded if they appear at the end of a line.
77 if (!requiresEncoding && (currentCharacter == '\t' || currentCharacter == ' ') && (isLastCharacter || lengthOfLineEndingAtIndex(input, inputLength, i + 1)))
78 requiresEncoding = true;
79
80 // End of line should be converted to CR-LF sequences.
81 if (!isLastCharacter) {
82 size_t lengthOfLineEnding = lengthOfLineEndingAtIndex(input, inputLength, i);
83 if (lengthOfLineEnding) {
84 out.append(crlfLineEnding, strlen(crlfLineEnding));
85 currentLineLength = 0;
86 i += (lengthOfLineEnding - 1); // -1 because we'll ++ in the for() above.
87 continue;
88 }
89 }
90
91 size_t lengthOfEncodedCharacter = 1;
92 if (requiresEncoding)
93 lengthOfEncodedCharacter += 2;
94 if (!isLastCharacter)
95 lengthOfEncodedCharacter += 1; // + 1 for the = (soft line break).
96
97 // Insert a soft line break if necessary.
98 if (currentLineLength + lengthOfEncodedCharacter > maximumLineLength) {
99 out.append('=');
100 out.append(crlfLineEnding, strlen(crlfLineEnding));
101 currentLineLength = 0;
102 }
103
104 // Finally, insert the actual character(s).
105 if (requiresEncoding) {
106 out.append('=');
107 out.append(upperNibbleToASCIIHexDigit(currentCharacter));
108 out.append(lowerNibbleToASCIIHexDigit(currentCharacter));
109 currentLineLength += 3;
110 } else {
111 out.append(currentCharacter);
112 currentLineLength++;
113 }
114 }
115}
116
117void quotedPrintableDecode(const Vector<char>& in, Vector<char>& out)
118{
119 quotedPrintableDecode(in.data(), in.size(), out);
120}
121
122void quotedPrintableDecode(const char* data, size_t dataLength, Vector<char>& out)
123{
124 out.clear();
125 if (!dataLength)
126 return;
127
128 for (size_t i = 0; i < dataLength; ++i) {
129 char currentCharacter = data[i];
130 if (currentCharacter != '=') {
131 out.append(currentCharacter);
132 continue;
133 }
134 // We are dealing with a '=xx' sequence.
135 if (dataLength - i < 3) {
136 // Unfinished = sequence, append as is.
137 out.append(currentCharacter);
138 continue;
139 }
140 char upperCharacter = data[++i];
141 char lowerCharacter = data[++i];
142 if (upperCharacter == '\r' && lowerCharacter == '\n')
143 continue;
144
145 if (!isASCIIHexDigit(upperCharacter) || !isASCIIHexDigit(lowerCharacter)) {
146 // Invalid sequence, = followed by non hex digits, just insert the characters as is.
147 out.append('=');
148 out.append(upperCharacter);
149 out.append(lowerCharacter);
150 continue;
151 }
152 out.append(static_cast<char>(toASCIIHexValue(upperCharacter, lowerCharacter)));
153 }
154}
155
156}
157