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