1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/350788890): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "url/url_canon.h"
11 #include "url/url_canon_internal.h"
12
13 // Query canonicalization in IE
14 // ----------------------------
15 // IE is very permissive for query parameters specified in links on the page
16 // (in contrast to links that it constructs itself based on form data). It does
17 // not unescape any character. It does not reject any escape sequence (be they
18 // invalid like "%2y" or freaky like %00).
19 //
20 // IE only escapes spaces and nothing else. Embedded NULLs, tabs (0x09),
21 // LF (0x0a), and CR (0x0d) are removed (this probably happens at an earlier
22 // layer since they are removed from all portions of the URL). All other
23 // characters are passed unmodified. Invalid UTF-16 sequences are preserved as
24 // well, with each character in the input being converted to UTF-8. It is the
25 // server's job to make sense of this invalid query.
26 //
27 // Invalid multibyte sequences (for example, invalid UTF-8 on a UTF-8 page)
28 // are converted to the invalid character and sent as unescaped UTF-8 (0xef,
29 // 0xbf, 0xbd). This may not be canonicalization, the parser may generate these
30 // strings before the URL handler ever sees them.
31 //
32 // Our query canonicalization
33 // --------------------------
34 // We escape all non-ASCII characters and control characters, like Firefox.
35 // This is more conformant to the URL spec, and there do not seem to be many
36 // problems relating to Firefox's behavior.
37 //
38 // Like IE, we will never unescape (although the application may want to try
39 // unescaping to present the user with a more understandable URL). We will
40 // replace all invalid sequences (including invalid UTF-16 sequences, which IE
41 // doesn't) with the "invalid character," and we will escape it.
42
43 namespace url {
44
45 namespace {
46
47 // Appends the given string to the output, escaping characters that do not
48 // match the given |type| in SharedCharTypes. This version will accept 8 or 16
49 // bit characters, but assumes that they have only 7-bit values. It also assumes
50 // that all UTF-8 values are correct, so doesn't bother checking
51 template<typename CHAR>
AppendRaw8BitQueryString(const CHAR * source,int length,CanonOutput * output)52 void AppendRaw8BitQueryString(const CHAR* source, int length,
53 CanonOutput* output) {
54 for (int i = 0; i < length; i++) {
55 if (!IsQueryChar(static_cast<unsigned char>(source[i])))
56 AppendEscapedChar(static_cast<unsigned char>(source[i]), output);
57 else // Doesn't need escaping.
58 output->push_back(static_cast<char>(source[i]));
59 }
60 }
61
62 // Runs the converter on the given UTF-8 input. Since the converter expects
63 // UTF-16, we have to convert first. The converter must be non-NULL.
RunConverter(const char * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)64 void RunConverter(const char* spec,
65 const Component& query,
66 CharsetConverter* converter,
67 CanonOutput* output) {
68 DCHECK(query.is_valid());
69 // This function will replace any misencoded values with the invalid
70 // character. This is what we want so we don't have to check for error.
71 RawCanonOutputW<1024> utf16;
72 ConvertUTF8ToUTF16(&spec[query.begin], static_cast<size_t>(query.len),
73 &utf16);
74 converter->ConvertFromUTF16(utf16.view(), output);
75 }
76
77 // Runs the converter with the given UTF-16 input. We don't have to do
78 // anything, but this overridden function allows us to use the same code
79 // for both UTF-8 and UTF-16 input.
RunConverter(const char16_t * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)80 void RunConverter(const char16_t* spec,
81 const Component& query,
82 CharsetConverter* converter,
83 CanonOutput* output) {
84 DCHECK(query.is_valid());
85 converter->ConvertFromUTF16(query.as_string_view_on(spec), output);
86 }
87
88 template <typename CHAR, typename UCHAR>
DoConvertToQueryEncoding(const CHAR * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)89 void DoConvertToQueryEncoding(const CHAR* spec,
90 const Component& query,
91 CharsetConverter* converter,
92 CanonOutput* output) {
93 if (converter) {
94 // Run the converter to get an 8-bit string, then append it, escaping
95 // necessary values.
96 RawCanonOutput<1024> eight_bit;
97 RunConverter(spec, query, converter, &eight_bit);
98 AppendRaw8BitQueryString(eight_bit.data(), eight_bit.length(), output);
99
100 } else {
101 // No converter, do our own UTF-8 conversion.
102 AppendStringOfType(&spec[query.begin], static_cast<size_t>(query.len),
103 CHAR_QUERY, output);
104 }
105 }
106
107 template<typename CHAR, typename UCHAR>
DoCanonicalizeQuery(const CHAR * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)108 void DoCanonicalizeQuery(const CHAR* spec,
109 const Component& query,
110 CharsetConverter* converter,
111 CanonOutput* output,
112 Component* out_query) {
113 if (!query.is_valid()) {
114 *out_query = Component();
115 return;
116 }
117
118 output->push_back('?');
119 out_query->begin = output->length();
120
121 DoConvertToQueryEncoding<CHAR, UCHAR>(spec, query, converter, output);
122
123 out_query->len = output->length() - out_query->begin;
124 }
125
126 } // namespace
127
CanonicalizeQuery(const char * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)128 void CanonicalizeQuery(const char* spec,
129 const Component& query,
130 CharsetConverter* converter,
131 CanonOutput* output,
132 Component* out_query) {
133 DoCanonicalizeQuery<char, unsigned char>(spec, query, converter,
134 output, out_query);
135 }
136
CanonicalizeQuery(const char16_t * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)137 void CanonicalizeQuery(const char16_t* spec,
138 const Component& query,
139 CharsetConverter* converter,
140 CanonOutput* output,
141 Component* out_query) {
142 DoCanonicalizeQuery<char16_t, char16_t>(spec, query, converter, output,
143 out_query);
144 }
145
ConvertUTF16ToQueryEncoding(const char16_t * input,const Component & query,CharsetConverter * converter,CanonOutput * output)146 void ConvertUTF16ToQueryEncoding(const char16_t* input,
147 const Component& query,
148 CharsetConverter* converter,
149 CanonOutput* output) {
150 DoConvertToQueryEncoding<char16_t, char16_t>(input, query, converter, output);
151 }
152
153 } // namespace url
154