/src/mozilla-central/xpcom/io/nsEscape.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsEscape.h" |
8 | | |
9 | | #include "mozilla/ArrayUtils.h" |
10 | | #include "mozilla/BinarySearch.h" |
11 | | #include "mozilla/CheckedInt.h" |
12 | | #include "mozilla/TextUtils.h" |
13 | | #include "nsTArray.h" |
14 | | #include "nsCRT.h" |
15 | | #include "plstr.h" |
16 | | #include "nsASCIIMask.h" |
17 | | |
18 | | static const char hexCharsUpper[] = "0123456789ABCDEF"; |
19 | | static const char hexCharsUpperLower[] = "0123456789ABCDEFabcdef"; |
20 | | |
21 | | static const int netCharType[256] = |
22 | | // clang-format off |
23 | | /* Bit 0 xalpha -- the alphas |
24 | | ** Bit 1 xpalpha -- as xalpha but |
25 | | ** converts spaces to plus and plus to %2B |
26 | | ** Bit 3 ... path -- as xalphas but doesn't escape '/' |
27 | | */ |
28 | | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
29 | | { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ |
30 | | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ |
31 | | 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ |
32 | | 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ |
33 | | 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ |
34 | | /* bits for '@' changed from 7 to 0 so '@' can be escaped */ |
35 | | /* in usernames and passwords in publishing. */ |
36 | | 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ |
37 | | 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ |
38 | | 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ |
39 | | 0, |
40 | | }; |
41 | | |
42 | | /* decode % escaped hex codes into character values |
43 | | */ |
44 | | #define UNHEX(C) \ |
45 | 773k | ((C >= '0' && C <= '9') ? C - '0' : \ |
46 | 773k | ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \ |
47 | 518k | ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0))) |
48 | | // clang-format on |
49 | | |
50 | 0 | #define IS_OK(C) (netCharType[((unsigned int)(C))] & (aFlags)) |
51 | 80.9M | #define HEX_ESCAPE '%' |
52 | | |
53 | | static const uint32_t ENCODE_MAX_LEN = 6; // %uABCD |
54 | | |
55 | | static uint32_t |
56 | | AppendPercentHex(char* aBuffer, unsigned char aChar) |
57 | 4.67M | { |
58 | 4.67M | uint32_t i = 0; |
59 | 4.67M | aBuffer[i++] = '%'; |
60 | 4.67M | aBuffer[i++] = hexCharsUpper[aChar >> 4]; // high nibble |
61 | 4.67M | aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low nibble |
62 | 4.67M | return i; |
63 | 4.67M | } |
64 | | |
65 | | static uint32_t |
66 | | AppendPercentHex(char16_t* aBuffer, char16_t aChar) |
67 | 0 | { |
68 | 0 | uint32_t i = 0; |
69 | 0 | aBuffer[i++] = '%'; |
70 | 0 | if (aChar & 0xff00) { |
71 | 0 | aBuffer[i++] = 'u'; |
72 | 0 | aBuffer[i++] = hexCharsUpper[aChar >> 12]; // high-byte high nibble |
73 | 0 | aBuffer[i++] = hexCharsUpper[(aChar >> 8) & 0xF]; // high-byte low nibble |
74 | 0 | } |
75 | 0 | aBuffer[i++] = hexCharsUpper[(aChar >> 4) & 0xF]; // low-byte high nibble |
76 | 0 | aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low-byte low nibble |
77 | 0 | return i; |
78 | 0 | } |
79 | | |
80 | | //---------------------------------------------------------------------------------------- |
81 | | char* |
82 | | nsEscape(const char* aStr, size_t aLength, size_t* aOutputLength, |
83 | | nsEscapeMask aFlags) |
84 | | //---------------------------------------------------------------------------------------- |
85 | 0 | { |
86 | 0 | if (!aStr) { |
87 | 0 | return nullptr; |
88 | 0 | } |
89 | 0 | |
90 | 0 | size_t charsToEscape = 0; |
91 | 0 |
|
92 | 0 | const unsigned char* src = (const unsigned char*)aStr; |
93 | 0 | for (size_t i = 0; i < aLength; ++i) { |
94 | 0 | if (!IS_OK(src[i])) { |
95 | 0 | charsToEscape++; |
96 | 0 | } |
97 | 0 | } |
98 | 0 |
|
99 | 0 | // calculate how much memory should be allocated |
100 | 0 | // original length + 2 bytes for each escaped character + terminating '\0' |
101 | 0 | // do the sum in steps to check for overflow |
102 | 0 | size_t dstSize = aLength + 1 + charsToEscape; |
103 | 0 | if (dstSize <= aLength) { |
104 | 0 | return nullptr; |
105 | 0 | } |
106 | 0 | dstSize += charsToEscape; |
107 | 0 | if (dstSize < aLength) { |
108 | 0 | return nullptr; |
109 | 0 | } |
110 | 0 | |
111 | 0 | // fail if we need more than 4GB |
112 | 0 | if (dstSize > UINT32_MAX) { |
113 | 0 | return nullptr; |
114 | 0 | } |
115 | 0 | |
116 | 0 | char* result = (char*)moz_xmalloc(dstSize); |
117 | 0 |
|
118 | 0 | unsigned char* dst = (unsigned char*)result; |
119 | 0 | src = (const unsigned char*)aStr; |
120 | 0 | if (aFlags == url_XPAlphas) { |
121 | 0 | for (size_t i = 0; i < aLength; ++i) { |
122 | 0 | unsigned char c = *src++; |
123 | 0 | if (IS_OK(c)) { |
124 | 0 | *dst++ = c; |
125 | 0 | } else if (c == ' ') { |
126 | 0 | *dst++ = '+'; /* convert spaces to pluses */ |
127 | 0 | } else { |
128 | 0 | *dst++ = HEX_ESCAPE; |
129 | 0 | *dst++ = hexCharsUpper[c >> 4]; /* high nibble */ |
130 | 0 | *dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */ |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } else { |
134 | 0 | for (size_t i = 0; i < aLength; ++i) { |
135 | 0 | unsigned char c = *src++; |
136 | 0 | if (IS_OK(c)) { |
137 | 0 | *dst++ = c; |
138 | 0 | } else { |
139 | 0 | *dst++ = HEX_ESCAPE; |
140 | 0 | *dst++ = hexCharsUpper[c >> 4]; /* high nibble */ |
141 | 0 | *dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */ |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | 0 |
|
146 | 0 | *dst = '\0'; /* tack on eos */ |
147 | 0 | if (aOutputLength) { |
148 | 0 | *aOutputLength = dst - (unsigned char*)result; |
149 | 0 | } |
150 | 0 |
|
151 | 0 | return result; |
152 | 0 | } |
153 | | |
154 | | //---------------------------------------------------------------------------------------- |
155 | | char* |
156 | | nsUnescape(char* aStr) |
157 | | //---------------------------------------------------------------------------------------- |
158 | 0 | { |
159 | 0 | nsUnescapeCount(aStr); |
160 | 0 | return aStr; |
161 | 0 | } |
162 | | |
163 | | //---------------------------------------------------------------------------------------- |
164 | | int32_t |
165 | | nsUnescapeCount(char* aStr) |
166 | | //---------------------------------------------------------------------------------------- |
167 | 20.0k | { |
168 | 20.0k | char* src = aStr; |
169 | 20.0k | char* dst = aStr; |
170 | 20.0k | |
171 | 20.0k | char c1[] = " "; |
172 | 20.0k | char c2[] = " "; |
173 | 20.0k | char* const pc1 = c1; |
174 | 20.0k | char* const pc2 = c2; |
175 | 20.0k | |
176 | 20.0k | if (!*src) { |
177 | 697 | // A null string was passed in. Nothing to escape. |
178 | 697 | // Returns early as the string might not actually be mutable with |
179 | 697 | // length 0. |
180 | 697 | return 0; |
181 | 697 | } |
182 | 19.3k | |
183 | 1.92M | while (*src) { |
184 | 1.90M | c1[0] = *(src + 1); |
185 | 1.90M | if (*(src + 1) == '\0') { |
186 | 19.1k | c2[0] = '\0'; |
187 | 1.88M | } else { |
188 | 1.88M | c2[0] = *(src + 2); |
189 | 1.88M | } |
190 | 1.90M | |
191 | 1.90M | if (*src != HEX_ESCAPE || PL_strpbrk(pc1, hexCharsUpperLower) == 0 || |
192 | 1.90M | PL_strpbrk(pc2, hexCharsUpperLower) == 0) { |
193 | 1.70M | *dst++ = *src++; |
194 | 1.70M | } else { |
195 | 205k | src++; /* walk over escape */ |
196 | 205k | if (*src) { |
197 | 205k | *dst = UNHEX(*src) << 4; |
198 | 205k | src++; |
199 | 205k | } |
200 | 205k | if (*src) { |
201 | 205k | *dst = (*dst + UNHEX(*src)); |
202 | 205k | src++; |
203 | 205k | } |
204 | 205k | dst++; |
205 | 205k | } |
206 | 1.90M | } |
207 | 19.3k | |
208 | 19.3k | *dst = 0; |
209 | 19.3k | return (int)(dst - aStr); |
210 | 19.3k | |
211 | 19.3k | } /* NET_UnEscapeCnt */ |
212 | | |
213 | | void |
214 | | nsAppendEscapedHTML(const nsACString& aSrc, nsACString& aDst) |
215 | 0 | { |
216 | 0 | // Preparation: aDst's length will increase by at least aSrc's length. If the |
217 | 0 | // addition overflows, we skip this, which is fine, and we'll likely abort |
218 | 0 | // while (infallibly) appending due to aDst becoming too large. |
219 | 0 | mozilla::CheckedInt<nsACString::size_type> newCapacity = aDst.Length(); |
220 | 0 | newCapacity += aSrc.Length(); |
221 | 0 | if (newCapacity.isValid()) { |
222 | 0 | aDst.SetCapacity(newCapacity.value()); |
223 | 0 | } |
224 | 0 |
|
225 | 0 | for (auto cur = aSrc.BeginReading(); cur != aSrc.EndReading(); cur++) { |
226 | 0 | if (*cur == '<') { |
227 | 0 | aDst.AppendLiteral("<"); |
228 | 0 | } else if (*cur == '>') { |
229 | 0 | aDst.AppendLiteral(">"); |
230 | 0 | } else if (*cur == '&') { |
231 | 0 | aDst.AppendLiteral("&"); |
232 | 0 | } else if (*cur == '"') { |
233 | 0 | aDst.AppendLiteral("""); |
234 | 0 | } else if (*cur == '\'') { |
235 | 0 | aDst.AppendLiteral("'"); |
236 | 0 | } else { |
237 | 0 | aDst.Append(*cur); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | //---------------------------------------------------------------------------------------- |
243 | | // |
244 | | // The following table encodes which characters needs to be escaped for which |
245 | | // parts of an URL. The bits are the "url components" in the enum EscapeMask, |
246 | | // see nsEscape.h. |
247 | | // |
248 | | // esc_Scheme = 1 |
249 | | // esc_Username = 2 |
250 | | // esc_Password = 4 |
251 | | // esc_Host = 8 |
252 | | // esc_Directory = 16 |
253 | | // esc_FileBaseName = 32 |
254 | | // esc_FileExtension = 64 |
255 | | // esc_Param = 128 |
256 | | // esc_Query = 256 |
257 | | // esc_Ref = 512 |
258 | | |
259 | | static const uint32_t EscapeChars[256] = |
260 | | // clang-format off |
261 | | // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
262 | | { |
263 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x |
264 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
265 | | 0,1023, 0, 512,1023, 0,1023, 624,1023,1023,1023,1023,1023,1023, 953, 784, // 2x !"#$%&'()*+,-./ |
266 | | 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008,1008, 0,1008, 0, 768, // 3x 0123456789:;<=>? |
267 | | 1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 4x @ABCDEFGHIJKLMNO |
268 | | 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008, 896,1008, 896,1023, // 5x PQRSTUVWXYZ[\]^_ |
269 | | 384,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 6x `abcdefghijklmno |
270 | | 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023, 0, // 7x pqrstuvwxyz{|}~ DEL |
271 | | 0 // 80 to FF are zero |
272 | | }; |
273 | | // clang-format on |
274 | | |
275 | | static uint16_t dontNeedEscape(unsigned char aChar, uint32_t aFlags) |
276 | 31.5M | { |
277 | 31.5M | return EscapeChars[(uint32_t)aChar] & aFlags; |
278 | 31.5M | } |
279 | | static uint16_t dontNeedEscape(uint16_t aChar, uint32_t aFlags) |
280 | 0 | { |
281 | 0 | return aChar < mozilla::ArrayLength(EscapeChars) ? |
282 | 0 | (EscapeChars[(uint32_t)aChar] & aFlags) : 0; |
283 | 0 | } |
284 | | |
285 | | //---------------------------------------------------------------------------------------- |
286 | | |
287 | | /** |
288 | | * Templated helper for URL escaping a portion of a string. |
289 | | * |
290 | | * @param aPart The pointer to the beginning of the portion of the string to |
291 | | * escape. |
292 | | * @param aPartLen The length of the string to escape. |
293 | | * @param aFlags Flags used to configure escaping. @see EscapeMask |
294 | | * @param aResult String that has the URL escaped portion appended to. Only |
295 | | * altered if the string is URL escaped or |esc_AlwaysCopy| is specified. |
296 | | * @param aDidAppend Indicates whether or not data was appended to |aResult|. |
297 | | * @return NS_ERROR_INVALID_ARG, NS_ERROR_OUT_OF_MEMORY on failure. |
298 | | */ |
299 | | template<class T> |
300 | | static nsresult |
301 | | T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen, |
302 | | uint32_t aFlags, const ASCIIMaskArray* aFilterMask, |
303 | | T& aResult, bool& aDidAppend) |
304 | 7.64M | { |
305 | 7.64M | typedef nsCharTraits<typename T::char_type> traits; |
306 | 7.64M | typedef typename traits::unsigned_char_type unsigned_char_type; |
307 | 7.64M | static_assert(sizeof(*aPart) == 1 || sizeof(*aPart) == 2, |
308 | 7.64M | "unexpected char type"); |
309 | 7.64M | |
310 | 7.64M | if (!aPart) { |
311 | 0 | MOZ_ASSERT_UNREACHABLE("null pointer"); |
312 | 0 | return NS_ERROR_INVALID_ARG; |
313 | 0 | } |
314 | 7.64M | |
315 | 7.64M | bool forced = !!(aFlags & esc_Forced); |
316 | 7.64M | bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII); |
317 | 7.64M | bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII); |
318 | 7.64M | bool writing = !!(aFlags & esc_AlwaysCopy); |
319 | 7.64M | bool colon = !!(aFlags & esc_Colon); |
320 | 7.64M | bool spaces = !!(aFlags & esc_Spaces); |
321 | 7.64M | |
322 | 7.64M | auto src = reinterpret_cast<const unsigned_char_type*>(aPart); |
323 | 7.64M | |
324 | 7.64M | typename T::char_type tempBuffer[100]; |
325 | 7.64M | unsigned int tempBufferPos = 0; |
326 | 7.64M | |
327 | 7.64M | bool previousIsNonASCII = false; |
328 | 39.1M | for (size_t i = 0; i < aPartLen; ++i) { |
329 | 31.5M | unsigned_char_type c = *src++; |
330 | 31.5M | |
331 | 31.5M | // If there is a filter, we wish to skip any characters which match it. |
332 | 31.5M | // This is needed so we don't perform an extra pass just to extract the |
333 | 31.5M | // filtered characters. |
334 | 31.5M | if (aFilterMask && mozilla::ASCIIMask::IsMasked(*aFilterMask, c)) { |
335 | 0 | if (!writing) { |
336 | 0 | if (!aResult.Append(aPart, i, mozilla::fallible)) { |
337 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
338 | 0 | } |
339 | 0 | writing = true; |
340 | 0 | } |
341 | 0 | continue; |
342 | 31.5M | } |
343 | 31.5M | |
344 | 31.5M | // if the char has not to be escaped or whatever follows % is |
345 | 31.5M | // a valid escaped string, just copy the char. |
346 | 31.5M | // |
347 | 31.5M | // Also the % will not be escaped until forced |
348 | 31.5M | // See bugzilla bug 61269 for details why we changed this |
349 | 31.5M | // |
350 | 31.5M | // And, we will not escape non-ascii characters if requested. |
351 | 31.5M | // On special request we will also escape the colon even when |
352 | 31.5M | // not covered by the matrix. |
353 | 31.5M | // ignoreAscii is not honored for control characters (C0 and DEL) |
354 | 31.5M | // |
355 | 31.5M | // And, we should escape the '|' character when it occurs after any |
356 | 31.5M | // non-ASCII character as it may be aPart of a multi-byte character. |
357 | 31.5M | // |
358 | 31.5M | // 0x20..0x7e are the valid ASCII characters. |
359 | 31.5M | if ((dontNeedEscape(c, aFlags) || (c == HEX_ESCAPE && !forced) |
360 | 31.5M | || (c > 0x7f && ignoreNonAscii) |
361 | 31.5M | || (c >= 0x20 && c < 0x7f && ignoreAscii)) |
362 | 31.5M | && !(c == ':' && colon) |
363 | 31.5M | && !(c == ' ' && spaces) |
364 | 31.5M | && !(previousIsNonASCII && c == '|' && !ignoreNonAscii)) { |
365 | 26.8M | if (writing) { |
366 | 3.40M | tempBuffer[tempBufferPos++] = c; |
367 | 3.40M | } |
368 | 26.8M | } else { /* do the escape magic */ |
369 | 4.67M | if (!writing) { |
370 | 514k | if (!aResult.Append(aPart, i, mozilla::fallible)) { |
371 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
372 | 0 | } |
373 | 514k | writing = true; |
374 | 514k | } |
375 | 4.67M | uint32_t len = ::AppendPercentHex(tempBuffer + tempBufferPos, c); |
376 | 4.67M | tempBufferPos += len; |
377 | 4.67M | MOZ_ASSERT(len <= ENCODE_MAX_LEN, "potential buffer overflow"); |
378 | 4.67M | } |
379 | 31.5M | |
380 | 31.5M | // Flush the temp buffer if it doesnt't have room for another encoded char. |
381 | 31.5M | if (tempBufferPos >= mozilla::ArrayLength(tempBuffer) - ENCODE_MAX_LEN) { |
382 | 130k | NS_ASSERTION(writing, "should be writing"); |
383 | 130k | if (!aResult.Append(tempBuffer, tempBufferPos, mozilla::fallible)) { |
384 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
385 | 0 | } |
386 | 130k | tempBufferPos = 0; |
387 | 130k | } |
388 | 31.5M | |
389 | 31.5M | previousIsNonASCII = (c > 0x7f); |
390 | 31.5M | } |
391 | 7.64M | if (writing) { |
392 | 514k | if (!aResult.Append(tempBuffer, tempBufferPos, mozilla::fallible)) { |
393 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
394 | 0 | } |
395 | 7.64M | } |
396 | 7.64M | aDidAppend = writing; |
397 | 7.64M | return NS_OK; |
398 | 7.64M | } Unified_cpp_xpcom_io0.cpp:nsresult T_EscapeURL<nsTSubstring<char> >(nsTSubstring<char>::char_type const*, unsigned long, unsigned int, std::__1::array<bool, 128ul> const*, nsTSubstring<char>&, bool&) Line | Count | Source | 304 | 7.64M | { | 305 | 7.64M | typedef nsCharTraits<typename T::char_type> traits; | 306 | 7.64M | typedef typename traits::unsigned_char_type unsigned_char_type; | 307 | 7.64M | static_assert(sizeof(*aPart) == 1 || sizeof(*aPart) == 2, | 308 | 7.64M | "unexpected char type"); | 309 | 7.64M | | 310 | 7.64M | if (!aPart) { | 311 | 0 | MOZ_ASSERT_UNREACHABLE("null pointer"); | 312 | 0 | return NS_ERROR_INVALID_ARG; | 313 | 0 | } | 314 | 7.64M | | 315 | 7.64M | bool forced = !!(aFlags & esc_Forced); | 316 | 7.64M | bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII); | 317 | 7.64M | bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII); | 318 | 7.64M | bool writing = !!(aFlags & esc_AlwaysCopy); | 319 | 7.64M | bool colon = !!(aFlags & esc_Colon); | 320 | 7.64M | bool spaces = !!(aFlags & esc_Spaces); | 321 | 7.64M | | 322 | 7.64M | auto src = reinterpret_cast<const unsigned_char_type*>(aPart); | 323 | 7.64M | | 324 | 7.64M | typename T::char_type tempBuffer[100]; | 325 | 7.64M | unsigned int tempBufferPos = 0; | 326 | 7.64M | | 327 | 7.64M | bool previousIsNonASCII = false; | 328 | 39.1M | for (size_t i = 0; i < aPartLen; ++i) { | 329 | 31.5M | unsigned_char_type c = *src++; | 330 | 31.5M | | 331 | 31.5M | // If there is a filter, we wish to skip any characters which match it. | 332 | 31.5M | // This is needed so we don't perform an extra pass just to extract the | 333 | 31.5M | // filtered characters. | 334 | 31.5M | if (aFilterMask && mozilla::ASCIIMask::IsMasked(*aFilterMask, c)) { | 335 | 0 | if (!writing) { | 336 | 0 | if (!aResult.Append(aPart, i, mozilla::fallible)) { | 337 | 0 | return NS_ERROR_OUT_OF_MEMORY; | 338 | 0 | } | 339 | 0 | writing = true; | 340 | 0 | } | 341 | 0 | continue; | 342 | 31.5M | } | 343 | 31.5M | | 344 | 31.5M | // if the char has not to be escaped or whatever follows % is | 345 | 31.5M | // a valid escaped string, just copy the char. | 346 | 31.5M | // | 347 | 31.5M | // Also the % will not be escaped until forced | 348 | 31.5M | // See bugzilla bug 61269 for details why we changed this | 349 | 31.5M | // | 350 | 31.5M | // And, we will not escape non-ascii characters if requested. | 351 | 31.5M | // On special request we will also escape the colon even when | 352 | 31.5M | // not covered by the matrix. | 353 | 31.5M | // ignoreAscii is not honored for control characters (C0 and DEL) | 354 | 31.5M | // | 355 | 31.5M | // And, we should escape the '|' character when it occurs after any | 356 | 31.5M | // non-ASCII character as it may be aPart of a multi-byte character. | 357 | 31.5M | // | 358 | 31.5M | // 0x20..0x7e are the valid ASCII characters. | 359 | 31.5M | if ((dontNeedEscape(c, aFlags) || (c == HEX_ESCAPE && !forced) | 360 | 31.5M | || (c > 0x7f && ignoreNonAscii) | 361 | 31.5M | || (c >= 0x20 && c < 0x7f && ignoreAscii)) | 362 | 31.5M | && !(c == ':' && colon) | 363 | 31.5M | && !(c == ' ' && spaces) | 364 | 31.5M | && !(previousIsNonASCII && c == '|' && !ignoreNonAscii)) { | 365 | 26.8M | if (writing) { | 366 | 3.40M | tempBuffer[tempBufferPos++] = c; | 367 | 3.40M | } | 368 | 26.8M | } else { /* do the escape magic */ | 369 | 4.67M | if (!writing) { | 370 | 514k | if (!aResult.Append(aPart, i, mozilla::fallible)) { | 371 | 0 | return NS_ERROR_OUT_OF_MEMORY; | 372 | 0 | } | 373 | 514k | writing = true; | 374 | 514k | } | 375 | 4.67M | uint32_t len = ::AppendPercentHex(tempBuffer + tempBufferPos, c); | 376 | 4.67M | tempBufferPos += len; | 377 | 4.67M | MOZ_ASSERT(len <= ENCODE_MAX_LEN, "potential buffer overflow"); | 378 | 4.67M | } | 379 | 31.5M | | 380 | 31.5M | // Flush the temp buffer if it doesnt't have room for another encoded char. | 381 | 31.5M | if (tempBufferPos >= mozilla::ArrayLength(tempBuffer) - ENCODE_MAX_LEN) { | 382 | 130k | NS_ASSERTION(writing, "should be writing"); | 383 | 130k | if (!aResult.Append(tempBuffer, tempBufferPos, mozilla::fallible)) { | 384 | 0 | return NS_ERROR_OUT_OF_MEMORY; | 385 | 0 | } | 386 | 130k | tempBufferPos = 0; | 387 | 130k | } | 388 | 31.5M | | 389 | 31.5M | previousIsNonASCII = (c > 0x7f); | 390 | 31.5M | } | 391 | 7.64M | if (writing) { | 392 | 514k | if (!aResult.Append(tempBuffer, tempBufferPos, mozilla::fallible)) { | 393 | 0 | return NS_ERROR_OUT_OF_MEMORY; | 394 | 0 | } | 395 | 7.64M | } | 396 | 7.64M | aDidAppend = writing; | 397 | 7.64M | return NS_OK; | 398 | 7.64M | } |
Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult T_EscapeURL<nsTSubstring<char16_t> >(nsTSubstring<char16_t>::char_type const*, unsigned long, unsigned int, std::__1::array<bool, 128ul> const*, nsTSubstring<char16_t>&, bool&) |
399 | | |
400 | | bool |
401 | | NS_EscapeURL(const char* aPart, int32_t aPartLen, uint32_t aFlags, |
402 | | nsACString& aResult) |
403 | 1.88M | { |
404 | 1.88M | if (aPartLen < 0) { |
405 | 349 | aPartLen = strlen(aPart); |
406 | 349 | } |
407 | 1.88M | |
408 | 1.88M | bool result = false; |
409 | 1.88M | nsresult rv = T_EscapeURL(aPart, aPartLen, aFlags, nullptr, aResult, result); |
410 | 1.88M | if (NS_FAILED(rv)) { |
411 | 0 | ::NS_ABORT_OOM(aResult.Length() * sizeof(nsACString::char_type)); |
412 | 0 | } |
413 | 1.88M | |
414 | 1.88M | return result; |
415 | 1.88M | } |
416 | | |
417 | | nsresult |
418 | | NS_EscapeURL(const nsACString& aStr, uint32_t aFlags, nsACString& aResult, |
419 | | const mozilla::fallible_t&) |
420 | 3.84M | { |
421 | 3.84M | bool appended = false; |
422 | 3.84M | nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, nullptr, aResult, appended); |
423 | 3.84M | if (NS_FAILED(rv)) { |
424 | 0 | aResult.Truncate(); |
425 | 0 | return rv; |
426 | 0 | } |
427 | 3.84M | |
428 | 3.84M | if (!appended) { |
429 | 3.84M | aResult = aStr; |
430 | 3.84M | } |
431 | 3.84M | |
432 | 3.84M | return rv; |
433 | 3.84M | } |
434 | | |
435 | | nsresult |
436 | | NS_EscapeAndFilterURL(const nsACString& aStr, uint32_t aFlags, |
437 | | const ASCIIMaskArray* aFilterMask, |
438 | | nsACString& aResult, const mozilla::fallible_t&) |
439 | 1.92M | { |
440 | 1.92M | bool appended = false; |
441 | 1.92M | nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aFilterMask, aResult, appended); |
442 | 1.92M | if (NS_FAILED(rv)) { |
443 | 0 | aResult.Truncate(); |
444 | 0 | return rv; |
445 | 0 | } |
446 | 1.92M | |
447 | 1.92M | if (!appended) { |
448 | 1.79M | if (!aResult.Assign(aStr, fallible)) { |
449 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
450 | 0 | } |
451 | 1.92M | } |
452 | 1.92M | |
453 | 1.92M | return rv; |
454 | 1.92M | } |
455 | | |
456 | | const nsAString& |
457 | | NS_EscapeURL(const nsAString& aStr, uint32_t aFlags, nsAString& aResult) |
458 | 0 | { |
459 | 0 | bool result = false; |
460 | 0 | nsresult rv = T_EscapeURL<nsAString>(aStr.Data(), aStr.Length(), aFlags, nullptr, aResult, result); |
461 | 0 |
|
462 | 0 | if (NS_FAILED(rv)) { |
463 | 0 | ::NS_ABORT_OOM(aResult.Length() * sizeof(nsAString::char_type)); |
464 | 0 | } |
465 | 0 |
|
466 | 0 | if (result) { |
467 | 0 | return aResult; |
468 | 0 | } |
469 | 0 | return aStr; |
470 | 0 | } |
471 | | |
472 | | // Starting at aStr[aStart] find the first index in aStr that matches any |
473 | | // character in aForbidden. Return false if not found. |
474 | | static bool |
475 | | FindFirstMatchFrom(const nsString& aStr, size_t aStart, |
476 | | const nsTArray<char16_t>& aForbidden, size_t* aIndex) |
477 | 0 | { |
478 | 0 | const size_t len = aForbidden.Length(); |
479 | 0 | for (size_t j = aStart, l = aStr.Length(); j < l; ++j) { |
480 | 0 | size_t unused; |
481 | 0 | if (mozilla::BinarySearch(aForbidden, 0, len, aStr[j], &unused)) { |
482 | 0 | *aIndex = j; |
483 | 0 | return true; |
484 | 0 | } |
485 | 0 | } |
486 | 0 | return false; |
487 | 0 | } |
488 | | |
489 | | const nsAString& |
490 | | NS_EscapeURL(const nsString& aStr, const nsTArray<char16_t>& aForbidden, |
491 | | nsAString& aResult) |
492 | 0 | { |
493 | 0 | bool didEscape = false; |
494 | 0 | for (size_t i = 0, strLen = aStr.Length(); i < strLen; ) { |
495 | 0 | size_t j; |
496 | 0 | if (MOZ_UNLIKELY(FindFirstMatchFrom(aStr, i, aForbidden, &j))) { |
497 | 0 | if (i == 0) { |
498 | 0 | didEscape = true; |
499 | 0 | aResult.Truncate(); |
500 | 0 | aResult.SetCapacity(aStr.Length()); |
501 | 0 | } |
502 | 0 | if (j != i) { |
503 | 0 | // The substring from 'i' up to 'j' that needs no escaping. |
504 | 0 | aResult.Append(nsDependentSubstring(aStr, i, j - i)); |
505 | 0 | } |
506 | 0 | char16_t buffer[ENCODE_MAX_LEN]; |
507 | 0 | uint32_t bufferLen = ::AppendPercentHex(buffer, aStr[j]); |
508 | 0 | MOZ_ASSERT(bufferLen <= ENCODE_MAX_LEN, "buffer overflow"); |
509 | 0 | aResult.Append(buffer, bufferLen); |
510 | 0 | i = j + 1; |
511 | 0 | } else { |
512 | 0 | if (MOZ_UNLIKELY(didEscape)) { |
513 | 0 | // The tail of the string that needs no escaping. |
514 | 0 | aResult.Append(nsDependentSubstring(aStr, i, strLen - i)); |
515 | 0 | } |
516 | 0 | break; |
517 | 0 | } |
518 | 0 | } |
519 | 0 | if (MOZ_UNLIKELY(didEscape)) { |
520 | 0 | return aResult; |
521 | 0 | } |
522 | 0 | return aStr; |
523 | 0 | } |
524 | | |
525 | | bool |
526 | | NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, |
527 | | nsACString& aResult) |
528 | 1.08M | { |
529 | 1.08M | bool didAppend = false; |
530 | 1.08M | nsresult rv = NS_UnescapeURL(aStr, aLen, aFlags, aResult, didAppend, |
531 | 1.08M | mozilla::fallible); |
532 | 1.08M | if (rv == NS_ERROR_OUT_OF_MEMORY) { |
533 | 0 | ::NS_ABORT_OOM(aLen * sizeof(nsACString::char_type)); |
534 | 0 | } |
535 | 1.08M | |
536 | 1.08M | return didAppend; |
537 | 1.08M | } |
538 | | |
539 | | nsresult |
540 | | NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, |
541 | | nsACString& aResult, bool& aDidAppend, |
542 | | const mozilla::fallible_t&) |
543 | 1.08M | { |
544 | 1.08M | if (!aStr) { |
545 | 0 | MOZ_ASSERT_UNREACHABLE("null pointer"); |
546 | 0 | return NS_ERROR_INVALID_ARG; |
547 | 0 | } |
548 | 1.08M | |
549 | 1.08M | MOZ_ASSERT(aResult.IsEmpty(), |
550 | 1.08M | "Passing a non-empty string as an out parameter!"); |
551 | 1.08M | |
552 | 1.08M | uint32_t len; |
553 | 1.08M | if (aLen < 0) { |
554 | 0 | size_t stringLength = strlen(aStr); |
555 | 0 | if (stringLength >= UINT32_MAX) { |
556 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
557 | 0 | } |
558 | 0 | len = stringLength; |
559 | 1.08M | } else { |
560 | 1.08M | len = aLen; |
561 | 1.08M | } |
562 | 1.08M | |
563 | 1.08M | bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII); |
564 | 1.08M | bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII); |
565 | 1.08M | bool writing = !!(aFlags & esc_AlwaysCopy); |
566 | 1.08M | bool skipControl = !!(aFlags & esc_SkipControl); |
567 | 1.08M | bool skipInvalidHostChar = !!(aFlags & esc_Host); |
568 | 1.08M | |
569 | 1.08M | unsigned char* destPtr; |
570 | 1.08M | uint32_t destPos; |
571 | 1.08M | |
572 | 1.08M | if (writing) { |
573 | 1.08M | if (!aResult.SetLength(len, mozilla::fallible)) { |
574 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
575 | 0 | } |
576 | 1.08M | destPos = 0; |
577 | 1.08M | destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting()); |
578 | 1.08M | } |
579 | 1.08M | |
580 | 1.08M | const char* last = aStr; |
581 | 1.08M | const char* end = aStr + len; |
582 | 1.08M | |
583 | 15.2M | for (const char* p = aStr; p < end; ++p) { |
584 | 14.1M | if (*p == HEX_ESCAPE && p + 2 < end) { |
585 | 181k | unsigned char c1 = *((unsigned char*)p + 1); |
586 | 181k | unsigned char c2 = *((unsigned char*)p + 2); |
587 | 181k | unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2); |
588 | 181k | if (mozilla::IsAsciiHexDigit(c1) && mozilla::IsAsciiHexDigit(c2) && |
589 | 181k | (!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') && |
590 | 181k | ((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) && |
591 | 181k | !(skipControl && |
592 | 55.5k | (c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) { |
593 | 55.5k | if (MOZ_UNLIKELY(!writing)) { |
594 | 0 | writing = true; |
595 | 0 | if (!aResult.SetLength(len, mozilla::fallible)) { |
596 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
597 | 0 | } |
598 | 0 | destPos = 0; |
599 | 0 | destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting()); |
600 | 0 | } |
601 | 55.5k | if (p > last) { |
602 | 48.5k | auto toCopy = p - last; |
603 | 48.5k | memcpy(destPtr + destPos, last, toCopy); |
604 | 48.5k | destPos += toCopy; |
605 | 48.5k | MOZ_ASSERT(destPos <= len); |
606 | 48.5k | last = p; |
607 | 48.5k | } |
608 | 55.5k | destPtr[destPos] = u; |
609 | 55.5k | destPos += 1; |
610 | 55.5k | MOZ_ASSERT(destPos <= len); |
611 | 55.5k | p += 2; |
612 | 55.5k | last += 3; |
613 | 55.5k | } |
614 | 181k | } |
615 | 14.1M | } |
616 | 1.08M | if (writing && last < end) { |
617 | 1.08M | auto toCopy = end - last; |
618 | 1.08M | memcpy(destPtr + destPos, last, toCopy); |
619 | 1.08M | destPos += toCopy; |
620 | 1.08M | MOZ_ASSERT(destPos <= len); |
621 | 1.08M | } |
622 | 1.08M | |
623 | 1.08M | if (writing) { |
624 | 1.08M | aResult.Truncate(destPos); |
625 | 1.08M | } |
626 | 1.08M | |
627 | 1.08M | aDidAppend = writing; |
628 | 1.08M | return NS_OK; |
629 | 1.08M | } |