Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 sts=4 et cindent: */
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 "IPCMessageUtils.h"
8 :
9 : #include "nsASCIIMask.h"
10 : #include "nsStandardURL.h"
11 : #include "nsCRT.h"
12 : #include "nsEscape.h"
13 : #include "nsIFile.h"
14 : #include "nsIObjectInputStream.h"
15 : #include "nsIObjectOutputStream.h"
16 : #include "nsIIDNService.h"
17 : #include "mozilla/Logging.h"
18 : #include "nsAutoPtr.h"
19 : #include "nsIURLParser.h"
20 : #include "nsNetCID.h"
21 : #include "mozilla/MemoryReporting.h"
22 : #include "mozilla/ipc/URIUtils.h"
23 : #include "mozilla/TextUtils.h"
24 : #include <algorithm>
25 : #include "nsContentUtils.h"
26 : #include "prprf.h"
27 : #include "nsReadableUtils.h"
28 : #include "mozilla/net/MozURL_ffi.h"
29 :
30 : //
31 : // setenv MOZ_LOG nsStandardURL:5
32 : //
33 : static LazyLogModule gStandardURLLog("nsStandardURL");
34 :
35 : // The Chromium code defines its own LOG macro which we don't want
36 : #undef LOG
37 : #define LOG(args) MOZ_LOG(gStandardURLLog, LogLevel::Debug, args)
38 : #undef LOG_ENABLED
39 : #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug)
40 :
41 : using namespace mozilla::ipc;
42 :
43 : namespace mozilla {
44 : namespace net {
45 :
46 : static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
47 : static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
48 :
49 : // This will always be initialized and destroyed on the main thread, but
50 : // can be safely used on other threads.
51 : nsIIDNService *nsStandardURL::gIDN = nullptr;
52 :
53 : // This value will only be updated on the main thread once. Worker threads
54 : // may race when reading this values, but that's OK because in the worst
55 : // case we will just dispatch a noop runnable to the main thread.
56 : bool nsStandardURL::gInitialized = false;
57 :
58 : const char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
59 : bool nsStandardURL::gPunycodeHost = true;
60 :
61 : // Invalid host characters
62 : // We still allow % because it is in the ID of addons.
63 : // Any percent encoded ASCII characters that are not allowed in the
64 : // hostname are not percent decoded, and will be parsed just fine.
65 : //
66 : // Note that the array below will be initialized at compile time,
67 : // so we do not need to "optimize" TestForInvalidHostCharacters.
68 : //
69 : constexpr bool TestForInvalidHostCharacters(char c)
70 : {
71 : // Testing for these:
72 : // CONTROL_CHARACTERS " #/:?@[\\]*<>|\"";
73 : return (c > 0 && c < 32) || // The control characters are [1, 31]
74 : c == ' ' || c == '#' || c == '/' || c == ':' || c == '?' ||
75 : c == '@' || c == '[' || c == '\\' || c == ']' || c == '*' ||
76 : c == '<' || c == '>' || c == '|' || c == '"';
77 : }
78 : constexpr ASCIIMaskArray sInvalidHostChars = CreateASCIIMask(TestForInvalidHostCharacters);
79 :
80 : //----------------------------------------------------------------------------
81 : // nsStandardURL::nsSegmentEncoder
82 : //----------------------------------------------------------------------------
83 :
84 4969 : nsStandardURL::nsSegmentEncoder::nsSegmentEncoder(const Encoding* encoding)
85 0 : : mEncoding(encoding)
86 : {
87 9938 : if (mEncoding == UTF_8_ENCODING) {
88 0 : mEncoding = nullptr;
89 : }
90 4969 : }
91 :
92 9966 : int32_t nsStandardURL::
93 : nsSegmentEncoder::EncodeSegmentCount(const char *str,
94 : const URLSegment &seg,
95 : int16_t mask,
96 : nsCString& result,
97 : bool &appended,
98 : uint32_t extraLen)
99 : {
100 : // extraLen is characters outside the segment that will be
101 : // added when the segment is not empty (like the @ following
102 : // a username).
103 9966 : appended = false;
104 0 : if (!str)
105 : return 0;
106 9966 : int32_t len = 0;
107 0 : if (seg.mLen > 0) {
108 0 : uint32_t pos = seg.mPos;
109 0 : len = seg.mLen;
110 :
111 : // first honor the origin charset if appropriate. as an optimization,
112 : // only do this if the segment is non-ASCII. Further, if mEncoding is
113 : // null, then the origin charset is UTF-8 and there is nothing to do.
114 13950 : nsAutoCString encBuf;
115 0 : if (mEncoding && !nsCRT::IsAscii(str + pos, len)) {
116 : // we have to encode this segment
117 : nsresult rv;
118 : const Encoding* ignored;
119 0 : Tie(rv, ignored) =
120 0 : mEncoding->Encode(Substring(str + pos, str + pos + len), encBuf);
121 0 : if (NS_SUCCEEDED(rv)) {
122 0 : str = encBuf.get();
123 0 : pos = 0;
124 0 : len = encBuf.Length();
125 : }
126 : // else some failure occurred... assume UTF-8 is ok.
127 : }
128 :
129 6975 : uint32_t initLen = result.Length();
130 :
131 : // now perform any required escaping
132 6975 : if (NS_EscapeURL(str + pos, len, mask, result)) {
133 0 : len = result.Length() - initLen;
134 0 : appended = true;
135 : }
136 6975 : else if (str == encBuf.get()) {
137 0 : result += encBuf; // append only!!
138 0 : len = encBuf.Length();
139 0 : appended = true;
140 : }
141 6975 : len += extraLen;
142 : }
143 : return len;
144 : }
145 :
146 0 : const nsACString &nsStandardURL::
147 : nsSegmentEncoder::EncodeSegment(const nsACString& str,
148 : int16_t mask,
149 : nsCString& result)
150 : {
151 : const char *text;
152 : bool encoded;
153 0 : EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
154 0 : if (encoded)
155 0 : return result;
156 : return str;
157 : }
158 :
159 : //----------------------------------------------------------------------------
160 : // nsStandardURL <public>
161 : //----------------------------------------------------------------------------
162 :
163 : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
164 1 : static StaticMutex gAllURLsMutex;
165 0 : static LinkedList<nsStandardURL> gAllURLs;
166 : #endif
167 :
168 5794 : nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
169 : : mDefaultPort(-1)
170 : , mPort(-1)
171 : , mDisplayHost(nullptr)
172 : , mURLType(URLTYPE_STANDARD)
173 : , mSupportsFileURL(aSupportsFileURL)
174 139056 : , mCheckedIfHostA(false)
175 : {
176 5794 : LOG(("Creating nsStandardURL @%p\n", this));
177 :
178 : // gInitialized changes value only once (false->true) on the main thread.
179 : // It's OK to race here because in the worst case we'll just
180 : // dispatch a noop runnable to the main thread.
181 5794 : MOZ_ASSERT(gInitialized);
182 :
183 : // default parser in case nsIStandardURL::Init is never called
184 5794 : mParser = net_GetStdURLParser();
185 :
186 : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
187 5794 : if (NS_IsMainThread()) {
188 0 : if (aTrackURL) {
189 0 : StaticMutexAutoLock lock(gAllURLsMutex);
190 0 : gAllURLs.insertBack(this);
191 : }
192 : }
193 : #endif
194 :
195 5794 : }
196 :
197 21980 : nsStandardURL::~nsStandardURL()
198 : {
199 4940 : LOG(("Destroying nsStandardURL @%p\n", this));
200 :
201 : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
202 : {
203 9880 : StaticMutexAutoLock lock(gAllURLsMutex);
204 0 : if (isInList()) {
205 0 : remove();
206 : }
207 : }
208 : #endif
209 9380 : }
210 :
211 : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
212 : struct DumpLeakedURLs {
213 : DumpLeakedURLs() = default;
214 : ~DumpLeakedURLs();
215 : };
216 :
217 0 : DumpLeakedURLs::~DumpLeakedURLs()
218 : {
219 0 : MOZ_ASSERT(NS_IsMainThread());
220 0 : StaticMutexAutoLock lock(gAllURLsMutex);
221 0 : if (!gAllURLs.isEmpty()) {
222 0 : printf("Leaked URLs:\n");
223 0 : for (auto url : gAllURLs) {
224 0 : url->PrintSpec();
225 : }
226 0 : gAllURLs.clear();
227 : }
228 0 : }
229 : #endif
230 :
231 : void
232 2 : nsStandardURL::InitGlobalObjects()
233 : {
234 2 : MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
235 :
236 2 : if (gInitialized) {
237 0 : return;
238 : }
239 :
240 1 : gInitialized = true;
241 :
242 1 : Preferences::AddBoolVarCache(&gPunycodeHost, "network.standard-url.punycode-host", true);
243 0 : nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
244 0 : if (serv) {
245 0 : NS_ADDREF(gIDN = serv.get());
246 : }
247 1 : MOZ_DIAGNOSTIC_ASSERT(gIDN);
248 :
249 : // Make sure nsURLHelper::InitGlobals() gets called on the main thread
250 2 : nsCOMPtr<nsIURLParser> parser = net_GetStdURLParser();
251 0 : MOZ_DIAGNOSTIC_ASSERT(parser);
252 : Unused << parser;
253 : }
254 :
255 : void
256 0 : nsStandardURL::ShutdownGlobalObjects()
257 : {
258 0 : MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
259 0 : NS_IF_RELEASE(gIDN);
260 :
261 : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
262 0 : if (gInitialized) {
263 : // This instanciates a dummy class, and will trigger the class
264 : // destructor when libxul is unloaded. This is equivalent to atexit(),
265 : // but gracefully handles dlclose().
266 0 : static DumpLeakedURLs d;
267 : }
268 : #endif
269 0 : }
270 :
271 : //----------------------------------------------------------------------------
272 : // nsStandardURL <private>
273 : //----------------------------------------------------------------------------
274 :
275 : void
276 2442 : nsStandardURL::Clear()
277 : {
278 2442 : mSpec.Truncate();
279 :
280 2442 : mPort = -1;
281 :
282 4884 : mScheme.Reset();
283 0 : mAuthority.Reset();
284 0 : mUsername.Reset();
285 0 : mPassword.Reset();
286 0 : mHost.Reset();
287 :
288 4884 : mPath.Reset();
289 0 : mFilepath.Reset();
290 0 : mDirectory.Reset();
291 0 : mBasename.Reset();
292 :
293 4884 : mExtension.Reset();
294 0 : mQuery.Reset();
295 0 : mRef.Reset();
296 :
297 2442 : InvalidateCache();
298 0 : }
299 :
300 : void
301 0 : nsStandardURL::InvalidateCache(bool invalidateCachedFile)
302 : {
303 0 : if (invalidateCachedFile) {
304 1 : mFile = nullptr;
305 : }
306 0 : }
307 :
308 : // Return the number of "dots" in the string, or -1 if invalid. Note that the
309 : // number of relevant entries in the bases/starts/ends arrays is number of
310 : // dots + 1.
311 : // Since the trailing dot is allowed, we pass and adjust "length".
312 : //
313 : // length is assumed to be <= host.Length(); the callers is responsible for that
314 : //
315 : // Note that the value returned is guaranteed to be in [-1, 3] range.
316 : inline int32_t
317 147 : ValidateIPv4Number(const nsACString& host,
318 : int32_t bases[4], int32_t dotIndex[3],
319 : bool& onlyBase10, int32_t& length)
320 : {
321 147 : MOZ_ASSERT(length <= (int32_t)host.Length());
322 0 : if (length <= 0) {
323 : return -1;
324 : }
325 :
326 147 : bool lastWasNumber = false; // We count on this being false for i == 0
327 0 : int32_t dotCount = 0;
328 0 : onlyBase10 = true;
329 :
330 187 : for (int32_t i = 0; i < length; i++) {
331 0 : char current = host[i];
332 0 : if (current == '.') {
333 0 : if (!lastWasNumber) { // A dot should not follow an X or a dot, or be first
334 : return -1;
335 : }
336 :
337 0 : if (dotCount > 0 && i == (length - 1)) { // Trailing dot is OK; shorten and return
338 0 : length--;
339 0 : return dotCount;
340 : }
341 :
342 0 : if (dotCount > 2) {
343 : return -1;
344 : }
345 0 : lastWasNumber = false;
346 0 : dotIndex[dotCount] = i;
347 0 : dotCount ++;
348 0 : } else if (current == 'X' || current == 'x') {
349 0 : if (!lastWasNumber || // An X should not follow an X or a dot or be first
350 0 : i == (length - 1) || // No trailing Xs allowed
351 0 : (dotCount == 0 && i != 1) || // If we had no dots, an X should be second
352 0 : host[i-1] != '0' || // X should always follow a 0. Guaranteed i > 0
353 : // as lastWasNumber is true
354 0 : (dotCount > 0 && host[i - 2] != '.')) { // And that zero follows a dot if it exists
355 : return -1;
356 : }
357 0 : lastWasNumber = false;
358 0 : bases[dotCount] = 16;
359 0 : onlyBase10 = false;
360 :
361 187 : } else if (current == '0') {
362 0 : if (i < length - 1 && // Trailing zero doesn't signal octal
363 0 : host[i + 1] != '.' && // Lone zero is not octal
364 0 : (i == 0 || host[i - 1] == '.')) { // Zero at start or following a dot is
365 : // a candidate for octal
366 0 : bases[dotCount] = 8; // This will turn to 16 above if X shows up
367 0 : onlyBase10 = false;
368 : }
369 : lastWasNumber = true;
370 :
371 187 : } else if (current >= '1' && current <= '7') {
372 : lastWasNumber = true;
373 :
374 147 : } else if (current >= '8' && current <= '9') {
375 0 : if (bases[dotCount] == 8) {
376 : return -1;
377 : }
378 : lastWasNumber = true;
379 :
380 294 : } else if ((current >= 'a' && current <= 'f') ||
381 0 : (current >= 'A' && current <= 'F')) {
382 0 : if (bases[dotCount] != 16) {
383 : return -1;
384 : }
385 : lastWasNumber = true;
386 :
387 : } else {
388 : return -1;
389 : }
390 : }
391 :
392 : return dotCount;
393 : }
394 :
395 : inline nsresult
396 0 : ParseIPv4Number10(const nsACString& input, uint32_t& number, uint32_t maxNumber)
397 : {
398 0 : uint64_t value = 0;
399 0 : const char* current = input.BeginReading();
400 0 : const char* end = input.EndReading();
401 0 : for (; current < end; ++current) {
402 0 : char c = *current;
403 0 : MOZ_ASSERT(c >= '0' && c <= '9');
404 0 : value *= 10;
405 0 : value += c - '0';
406 : }
407 0 : if (value <= maxNumber) {
408 0 : number = value;
409 0 : return NS_OK;
410 : }
411 :
412 : // The error case
413 0 : number = 0;
414 0 : return NS_ERROR_FAILURE;
415 : }
416 :
417 : inline nsresult
418 0 : ParseIPv4Number(const nsACString& input, int32_t base, uint32_t& number, uint32_t maxNumber)
419 : {
420 : // Accumulate in the 64-bit value
421 0 : uint64_t value = 0;
422 0 : const char* current = input.BeginReading();
423 0 : const char* end = input.EndReading();
424 0 : switch(base) {
425 : case 16:
426 0 : ++current;
427 : MOZ_FALLTHROUGH;
428 : case 8:
429 0 : ++current;
430 0 : break;
431 : case 10:
432 : default:
433 : break;
434 : }
435 0 : for (; current < end; ++current) {
436 0 : value *= base;
437 0 : char c = *current;
438 0 : MOZ_ASSERT((base == 10 && isdigit(c)) ||
439 : (base == 8 && c >= '0' && c <= '7') ||
440 : (base == 16 && isxdigit(c)));
441 0 : if (isdigit(c)) {
442 0 : value += c - '0';
443 0 : } else if (c >= 'a' && c <= 'f') {
444 0 : value += c - 'a' + 10;
445 0 : } else if (c >= 'A' && c <= 'F') {
446 0 : value += c - 'A' + 10;
447 : }
448 : }
449 :
450 0 : if (value <= maxNumber) {
451 0 : number = value;
452 0 : return NS_OK;
453 : }
454 :
455 : // The error case
456 0 : number = 0;
457 0 : return NS_ERROR_FAILURE;
458 : }
459 :
460 : // IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser
461 : /* static */ nsresult
462 147 : nsStandardURL::NormalizeIPv4(const nsACString& host, nsCString& result)
463 : {
464 147 : int32_t bases[4] = {10,10,10,10};
465 0 : bool onlyBase10 = true; // Track this as a special case
466 : int32_t dotIndex[3]; // The positions of the dots in the string
467 :
468 : // The length may be adjusted by ValidateIPv4Number (ignoring the trailing period)
469 : // so use "length", rather than host.Length() after that call.
470 147 : int32_t length = static_cast<int32_t>(host.Length());
471 : int32_t dotCount = ValidateIPv4Number(host, bases, dotIndex,
472 147 : onlyBase10, length);
473 0 : if (dotCount < 0 || length <= 0) {
474 : return NS_ERROR_FAILURE;
475 : }
476 :
477 : // Max values specified by the spec
478 : static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu,
479 : 0xffffu, 0xffu};
480 : uint32_t ipv4;
481 0 : int32_t start = (dotCount > 0 ? dotIndex[dotCount-1] + 1 : 0);
482 :
483 : nsresult res;
484 : // Doing a special case for all items being base 10 gives ~35% speedup
485 0 : res = (onlyBase10 ?
486 0 : ParseIPv4Number10(Substring(host, start, length - start),
487 0 : ipv4, upperBounds[dotCount]) :
488 0 : ParseIPv4Number(Substring(host, start, length - start),
489 : bases[dotCount],
490 0 : ipv4, upperBounds[dotCount]));
491 0 : if (NS_FAILED(res)) {
492 : return NS_ERROR_FAILURE;
493 : }
494 :
495 : int32_t lastUsed = -1;
496 0 : for (int32_t i = 0; i < dotCount; i++) {
497 : uint32_t number;
498 0 : start = lastUsed + 1;
499 0 : lastUsed = dotIndex[i];
500 0 : res = (onlyBase10 ?
501 0 : ParseIPv4Number10(Substring(host, start, lastUsed - start),
502 : number, 255) :
503 0 : ParseIPv4Number(Substring(host, start, lastUsed - start),
504 0 : bases[i], number, 255));
505 0 : if (NS_FAILED(res)) {
506 0 : return NS_ERROR_FAILURE;
507 : }
508 0 : ipv4 += number << (8 * (3 - i));
509 : }
510 :
511 : uint8_t ipSegments[4];
512 0 : NetworkEndian::writeUint32(ipSegments, ipv4);
513 0 : result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1],
514 0 : ipSegments[2], ipSegments[3]);
515 0 : return NS_OK;
516 : }
517 :
518 : nsresult
519 772 : nsStandardURL::NormalizeIDN(const nsACString& host, nsCString& result)
520 : {
521 : // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8,
522 : // then make sure it is normalized per IDN.
523 :
524 : // this function returns true if normalization succeeds.
525 :
526 772 : result.Truncate();
527 : nsresult rv;
528 :
529 772 : if (!gIDN) {
530 : return NS_ERROR_UNEXPECTED;
531 : }
532 :
533 : bool isAscii;
534 772 : nsAutoCString normalized;
535 0 : rv = gIDN->ConvertToDisplayIDN(host, &isAscii, normalized);
536 0 : if (NS_FAILED(rv)) {
537 : return rv;
538 : }
539 :
540 : // The result is ASCII. No need to convert to ACE.
541 772 : if (isAscii) {
542 0 : result = normalized;
543 0 : mCheckedIfHostA = true;
544 0 : mDisplayHost.Truncate();
545 0 : return NS_OK;
546 : }
547 :
548 0 : rv = gIDN->ConvertUTF8toACE(normalized, result);
549 0 : if (NS_FAILED(rv)) {
550 : return rv;
551 : }
552 :
553 0 : mCheckedIfHostA = true;
554 0 : mDisplayHost = normalized;
555 :
556 0 : return NS_OK;
557 : }
558 :
559 : bool
560 772 : nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length)
561 : {
562 772 : if (!host || !*host) {
563 : // Should not be NULL or empty string
564 : return false;
565 : }
566 :
567 772 : if (length != strlen(host)) {
568 : // Embedded null
569 : return false;
570 : }
571 :
572 772 : bool openBracket = host[0] == '[';
573 0 : bool closeBracket = host[length - 1] == ']';
574 :
575 772 : if (openBracket && closeBracket) {
576 0 : return net_IsValidIPv6Addr(host + 1, length - 2);
577 : }
578 :
579 772 : if (openBracket || closeBracket) {
580 : // Fail if only one of the brackets is present
581 : return false;
582 : }
583 :
584 772 : const char* end = host + length;
585 0 : const char* iter = host;
586 0 : for (; iter != end && *iter; ++iter) {
587 0 : if (ASCIIMask::IsMasked(sInvalidHostChars, *iter)) {
588 : return false;
589 : }
590 : }
591 : return true;
592 : }
593 :
594 : void
595 2212 : nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
596 : {
597 2212 : net_CoalesceDirs(coalesceFlag, path);
598 0 : int32_t newLen = strlen(path);
599 0 : if (newLen < mPath.mLen) {
600 0 : int32_t diff = newLen - mPath.mLen;
601 0 : mPath.mLen = newLen;
602 0 : mDirectory.mLen += diff;
603 0 : mFilepath.mLen += diff;
604 0 : ShiftFromBasename(diff);
605 : }
606 2212 : }
607 :
608 : uint32_t
609 10298 : nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str,
610 : const URLSegment &segInput, URLSegment &segOutput,
611 : const nsCString *escapedStr,
612 : bool useEscaped, int32_t *diff)
613 : {
614 10298 : MOZ_ASSERT(segInput.mLen == segOutput.mLen);
615 :
616 10298 : if (diff) *diff = 0;
617 :
618 10298 : if (segInput.mLen > 0) {
619 0 : if (useEscaped) {
620 0 : MOZ_ASSERT(diff);
621 0 : segOutput.mLen = escapedStr->Length();
622 0 : *diff = segOutput.mLen - segInput.mLen;
623 0 : memcpy(buf + i, escapedStr->get(), segOutput.mLen);
624 : } else {
625 9332 : memcpy(buf + i, str + segInput.mPos, segInput.mLen);
626 : }
627 10104 : segOutput.mPos = i;
628 0 : i += segOutput.mLen;
629 : } else {
630 194 : segOutput.mPos = i;
631 : }
632 10298 : return i;
633 : }
634 :
635 : uint32_t
636 0 : nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len)
637 : {
638 4884 : memcpy(buf + i, str, len);
639 0 : return i + len;
640 : }
641 :
642 : // basic algorithm:
643 : // 1- escape url segments (for improved GetSpec efficiency)
644 : // 2- allocate spec buffer
645 : // 3- write url segments
646 : // 4- update url segment positions and lengths
647 : nsresult
648 2442 : nsStandardURL::BuildNormalizedSpec(const char *spec,
649 : const Encoding* encoding)
650 : {
651 : // Assumptions: all member URLSegments must be relative the |spec| argument
652 : // passed to this function.
653 :
654 : // buffers for holding escaped url segments (these will remain empty unless
655 : // escaping is required).
656 12210 : nsAutoCString encUsername, encPassword, encHost, encDirectory,
657 0 : encBasename, encExtension, encQuery, encRef;
658 0 : bool useEncUsername, useEncPassword, useEncHost = false,
659 : useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef;
660 4884 : nsAutoCString portbuf;
661 :
662 : //
663 : // escape each URL segment, if necessary, and calculate approximate normalized
664 : // spec length.
665 : //
666 : // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
667 :
668 2442 : uint32_t approxLen = 0;
669 :
670 : // the scheme is already ASCII
671 2442 : if (mScheme.mLen > 0)
672 0 : approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
673 :
674 : // encode URL segments; convert UTF-8 to origin charset and possibly escape.
675 : // results written to encXXX variables only if |spec| is not already in the
676 : // appropriate encoding.
677 : {
678 2442 : nsSegmentEncoder encoder;
679 0 : nsSegmentEncoder queryEncoder(encoding);
680 : // Items using an extraLen of 1 don't add anything unless mLen > 0
681 : // Username@
682 2442 : approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1);
683 : // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
684 2442 : if (mPassword.mLen >= 0)
685 0 : approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword);
686 : // mHost is handled differently below due to encoding differences
687 2442 : MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
688 0 : if (mPort != -1 && mPort != mDefaultPort)
689 : {
690 : // :port
691 0 : portbuf.AppendInt(mPort);
692 0 : approxLen += portbuf.Length() + 1;
693 : }
694 :
695 2442 : approxLen += 1; // reserve space for possible leading '/' - may not be needed
696 : // Should just use mPath? These are pessimistic, and thus waste space
697 2442 : approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1);
698 0 : approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename);
699 0 : approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
700 :
701 : // These next ones *always* add their leading character even if length is 0
702 : // Handles items like "http://#"
703 : // ?query
704 2442 : if (mQuery.mLen >= 0)
705 0 : approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery);
706 : // #ref
707 :
708 2442 : if (mRef.mLen >= 0) {
709 0 : approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref,
710 0 : encRef, useEncRef);
711 : }
712 : }
713 :
714 : // do not escape the hostname, if IPv6 address literal, mHost will
715 : // already point to a [ ] delimited IPv6 address literal.
716 : // However, perform Unicode normalization on it, as IDN does.
717 : // Note that we don't disallow URLs without a host - file:, etc
718 2442 : if (mHost.mLen > 0) {
719 0 : nsAutoCString tempHost;
720 0 : NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost);
721 0 : if (tempHost.Contains('\0'))
722 0 : return NS_ERROR_MALFORMED_URI; // null embedded in hostname
723 0 : if (tempHost.Contains(' '))
724 : return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname
725 772 : nsresult rv = NormalizeIDN(tempHost, encHost);
726 0 : if (NS_FAILED(rv)) {
727 : return rv;
728 : }
729 1298 : if (!SegmentIs(spec, mScheme, "resource") &&
730 0 : !SegmentIs(spec, mScheme, "chrome")) {
731 0 : nsAutoCString ipString;
732 0 : if (encHost.Length() > 0 &&
733 0 : encHost.First() == '[' && encHost.Last() == ']' &&
734 0 : ValidIPv6orHostname(encHost.get(), encHost.Length())) {
735 0 : rv = (nsresult) rusturl_parse_ipv6addr(&encHost, &ipString);
736 0 : if (NS_FAILED(rv)) {
737 0 : return rv;
738 : }
739 : encHost = ipString;
740 147 : } else if (NS_SUCCEEDED(NormalizeIPv4(encHost, ipString))) {
741 : encHost = ipString;
742 : }
743 : }
744 :
745 : // NormalizeIDN always copies, if the call was successful.
746 772 : useEncHost = true;
747 0 : approxLen += encHost.Length();
748 :
749 772 : if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) {
750 : return NS_ERROR_MALFORMED_URI;
751 : }
752 : } else {
753 : // empty host means empty mDisplayHost
754 1670 : mDisplayHost.Truncate();
755 0 : mCheckedIfHostA = true;
756 : }
757 :
758 : // We must take a copy of every single segment because they are pointing to
759 : // the |spec| while we are changing their value, in case we must use
760 : // encoded strings.
761 2442 : URLSegment username(mUsername);
762 0 : URLSegment password(mPassword);
763 0 : URLSegment host(mHost);
764 0 : URLSegment path(mPath);
765 0 : URLSegment directory(mDirectory);
766 0 : URLSegment basename(mBasename);
767 0 : URLSegment extension(mExtension);
768 0 : URLSegment query(mQuery);
769 0 : URLSegment ref(mRef);
770 :
771 : // The encoded string could be longer than the original input, so we need
772 : // to check the final URI isn't longer than the max length.
773 2442 : if (approxLen + 1 > (uint32_t) net_GetURLMaxLength()) {
774 : return NS_ERROR_MALFORMED_URI;
775 : }
776 :
777 : //
778 : // generate the normalized URL string
779 : //
780 : // approxLen should be correct or 1 high
781 2442 : if (!mSpec.SetLength(approxLen+1, fallible)) // buf needs a trailing '\0' below
782 : return NS_ERROR_OUT_OF_MEMORY;
783 : char *buf;
784 2442 : mSpec.BeginWriting(buf);
785 0 : uint32_t i = 0;
786 0 : int32_t diff = 0;
787 :
788 2442 : if (mScheme.mLen > 0) {
789 0 : i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
790 0 : net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
791 0 : i = AppendToBuf(buf, i, "://", 3);
792 : }
793 :
794 : // record authority starting position
795 2442 : mAuthority.mPos = i;
796 :
797 : // append authority
798 2442 : if (mUsername.mLen > 0) {
799 0 : i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
800 0 : &encUsername, useEncUsername, &diff);
801 0 : ShiftFromPassword(diff);
802 0 : if (password.mLen > 0) {
803 0 : buf[i++] = ':';
804 0 : i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
805 0 : &encPassword, useEncPassword, &diff);
806 0 : ShiftFromHost(diff);
807 : } else {
808 0 : mPassword.mLen = -1;
809 : }
810 0 : buf[i++] = '@';
811 : }
812 2442 : if (host.mLen > 0) {
813 0 : i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
814 0 : &diff);
815 0 : ShiftFromPath(diff);
816 :
817 772 : net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
818 0 : MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
819 0 : if (mPort != -1 && mPort != mDefaultPort) {
820 0 : buf[i++] = ':';
821 : // Already formatted while building approxLen
822 0 : i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
823 : }
824 : }
825 :
826 : // record authority length
827 2442 : mAuthority.mLen = i - mAuthority.mPos;
828 :
829 : // path must always start with a "/"
830 2442 : if (mPath.mLen <= 0) {
831 0 : LOG(("setting path=/"));
832 0 : mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
833 0 : mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
834 : // basename must exist, even if empty (bug 113508)
835 52 : mBasename.mPos = i+1;
836 0 : mBasename.mLen = 0;
837 0 : buf[i++] = '/';
838 : }
839 : else {
840 2390 : uint32_t leadingSlash = 0;
841 0 : if (spec[path.mPos] != '/') {
842 0 : LOG(("adding leading slash to path\n"));
843 0 : leadingSlash = 1;
844 0 : buf[i++] = '/';
845 : // basename must exist, even if empty (bugs 113508, 429347)
846 0 : if (mBasename.mLen == -1) {
847 0 : mBasename.mPos = basename.mPos = i;
848 0 : mBasename.mLen = basename.mLen = 0;
849 : }
850 : }
851 :
852 : // record corrected (file)path starting position
853 2390 : mPath.mPos = mFilepath.mPos = i - leadingSlash;
854 :
855 2390 : i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory,
856 0 : &encDirectory, useEncDirectory, &diff);
857 0 : ShiftFromBasename(diff);
858 :
859 : // the directory must end with a '/'
860 2390 : if (buf[i-1] != '/') {
861 0 : buf[i++] = '/';
862 0 : mDirectory.mLen++;
863 : }
864 :
865 2390 : i = AppendSegmentToBuf(buf, i, spec, basename, mBasename,
866 0 : &encBasename, useEncBasename, &diff);
867 0 : ShiftFromExtension(diff);
868 :
869 : // make corrections to directory segment if leadingSlash
870 2390 : if (leadingSlash) {
871 0 : mDirectory.mPos = mPath.mPos;
872 0 : if (mDirectory.mLen >= 0)
873 0 : mDirectory.mLen += leadingSlash;
874 : else
875 0 : mDirectory.mLen = 1;
876 : }
877 :
878 2390 : if (mExtension.mLen >= 0) {
879 0 : buf[i++] = '.';
880 0 : i = AppendSegmentToBuf(buf, i, spec, extension, mExtension,
881 0 : &encExtension, useEncExtension, &diff);
882 0 : ShiftFromQuery(diff);
883 : }
884 : // calculate corrected filepath length
885 2390 : mFilepath.mLen = i - mFilepath.mPos;
886 :
887 2390 : if (mQuery.mLen >= 0) {
888 0 : buf[i++] = '?';
889 0 : i = AppendSegmentToBuf(buf, i, spec, query, mQuery,
890 : &encQuery, useEncQuery,
891 10 : &diff);
892 0 : ShiftFromRef(diff);
893 : }
894 2390 : if (mRef.mLen >= 0) {
895 0 : buf[i++] = '#';
896 0 : i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
897 0 : &diff);
898 : }
899 : // calculate corrected path length
900 2390 : mPath.mLen = i - mPath.mPos;
901 : }
902 :
903 2442 : buf[i] = '\0';
904 :
905 : // https://url.spec.whatwg.org/#path-state (1.4.1.2)
906 : // https://url.spec.whatwg.org/#windows-drive-letter
907 2442 : if (SegmentIs(buf, mScheme, "file")) {
908 0 : char* path = &buf[mPath.mPos];
909 0 : if (mPath.mLen >= 3 && path[0] == '/'
910 0 : && IsAsciiAlpha(path[1])
911 0 : && path[2] == '|') {
912 0 : buf[mPath.mPos + 2] = ':';
913 : }
914 : }
915 :
916 2442 : if (mDirectory.mLen > 1) {
917 0 : netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
918 0 : if (SegmentIs(buf,mScheme,"ftp")) {
919 0 : coalesceFlag = (netCoalesceFlags) (coalesceFlag
920 : | NET_COALESCE_ALLOW_RELATIVE_ROOT
921 : | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
922 : }
923 2212 : CoalescePath(coalesceFlag, buf + mDirectory.mPos);
924 : }
925 2442 : mSpec.SetLength(strlen(buf));
926 0 : NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
927 0 : MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
928 : "The spec should never be this long, we missed a check.");
929 :
930 : return NS_OK;
931 : }
932 :
933 : bool
934 6748 : nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
935 : {
936 : // one or both may be null
937 6748 : if (!val || mSpec.IsEmpty())
938 0 : return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
939 1 : if (seg.mLen < 0)
940 : return false;
941 : // if the first |seg.mLen| chars of |val| match, then |val| must
942 : // also be null terminated at |seg.mLen|.
943 6748 : if (ignoreCase)
944 0 : return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
945 0 : && (val[seg.mLen] == '\0');
946 :
947 9824 : return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen) &&
948 0 : (val[seg.mLen] == '\0');
949 : }
950 :
951 : bool
952 5952 : nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase)
953 : {
954 : // one or both may be null
955 5952 : if (!val || !spec)
956 0 : return (!val && (!spec || seg.mLen < 0));
957 1 : if (seg.mLen < 0)
958 : return false;
959 : // if the first |seg.mLen| chars of |val| match, then |val| must
960 : // also be null terminated at |seg.mLen|.
961 5952 : if (ignoreCase)
962 0 : return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
963 0 : && (val[seg.mLen] == '\0');
964 :
965 5952 : return !strncmp(spec + seg.mPos, val, seg.mLen) && (val[seg.mLen] == '\0');
966 : }
967 :
968 : bool
969 10823 : nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase)
970 : {
971 10823 : if (seg1.mLen != seg2.mLen)
972 : return false;
973 10659 : if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
974 : return true; // both are empty
975 5893 : if (!val)
976 : return false;
977 5893 : if (ignoreCase)
978 0 : return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
979 :
980 5893 : return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
981 : }
982 :
983 : int32_t
984 85 : nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen)
985 : {
986 85 : if (val && valLen) {
987 0 : if (len == 0)
988 0 : mSpec.Insert(val, pos, valLen);
989 : else
990 0 : mSpec.Replace(pos, len, nsDependentCString(val, valLen));
991 0 : return valLen - len;
992 : }
993 :
994 : // else remove the specified segment
995 0 : mSpec.Cut(pos, len);
996 0 : return -int32_t(len);
997 : }
998 :
999 : int32_t
1000 0 : nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val)
1001 : {
1002 0 : if (len == 0)
1003 0 : mSpec.Insert(val, pos);
1004 : else
1005 0 : mSpec.Replace(pos, len, val);
1006 0 : return val.Length() - len;
1007 : }
1008 :
1009 : nsresult
1010 2442 : nsStandardURL::ParseURL(const char *spec, int32_t specLen)
1011 : {
1012 : nsresult rv;
1013 :
1014 2442 : if (specLen > net_GetURLMaxLength()) {
1015 : return NS_ERROR_MALFORMED_URI;
1016 : }
1017 :
1018 : //
1019 : // parse given URL string
1020 : //
1021 4884 : rv = mParser->ParseURL(spec, specLen,
1022 : &mScheme.mPos, &mScheme.mLen,
1023 : &mAuthority.mPos, &mAuthority.mLen,
1024 4884 : &mPath.mPos, &mPath.mLen);
1025 0 : if (NS_FAILED(rv)) return rv;
1026 :
1027 : #ifdef DEBUG
1028 2442 : if (mScheme.mLen <= 0) {
1029 0 : printf("spec=%s\n", spec);
1030 0 : NS_WARNING("malformed url: no scheme");
1031 : }
1032 : #endif
1033 :
1034 2442 : if (mAuthority.mLen > 0) {
1035 0 : rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
1036 : &mUsername.mPos, &mUsername.mLen,
1037 : &mPassword.mPos, &mPassword.mLen,
1038 : &mHost.mPos, &mHost.mLen,
1039 1544 : &mPort);
1040 0 : if (NS_FAILED(rv)) return rv;
1041 :
1042 : // Don't allow mPort to be set to this URI's default port
1043 772 : if (mPort == mDefaultPort)
1044 0 : mPort = -1;
1045 :
1046 772 : mUsername.mPos += mAuthority.mPos;
1047 0 : mPassword.mPos += mAuthority.mPos;
1048 0 : mHost.mPos += mAuthority.mPos;
1049 : }
1050 :
1051 2442 : if (mPath.mLen > 0)
1052 0 : rv = ParsePath(spec, mPath.mPos, mPath.mLen);
1053 :
1054 : return rv;
1055 : }
1056 :
1057 : nsresult
1058 2390 : nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
1059 : {
1060 2390 : LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
1061 :
1062 2390 : if (pathLen > net_GetURLMaxLength()) {
1063 : return NS_ERROR_MALFORMED_URI;
1064 : }
1065 :
1066 4780 : nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
1067 : &mFilepath.mPos, &mFilepath.mLen,
1068 : &mQuery.mPos, &mQuery.mLen,
1069 4780 : &mRef.mPos, &mRef.mLen);
1070 0 : if (NS_FAILED(rv)) return rv;
1071 :
1072 2390 : mFilepath.mPos += pathPos;
1073 0 : mQuery.mPos += pathPos;
1074 0 : mRef.mPos += pathPos;
1075 :
1076 2390 : if (mFilepath.mLen > 0) {
1077 0 : rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
1078 : &mDirectory.mPos, &mDirectory.mLen,
1079 : &mBasename.mPos, &mBasename.mLen,
1080 4780 : &mExtension.mPos, &mExtension.mLen);
1081 0 : if (NS_FAILED(rv)) return rv;
1082 :
1083 2390 : mDirectory.mPos += mFilepath.mPos;
1084 0 : mBasename.mPos += mFilepath.mPos;
1085 0 : mExtension.mPos += mFilepath.mPos;
1086 : }
1087 : return NS_OK;
1088 : }
1089 :
1090 : char *
1091 657 : nsStandardURL::AppendToSubstring(uint32_t pos,
1092 : int32_t len,
1093 : const char *tail)
1094 : {
1095 : // Verify pos and length are within boundaries
1096 657 : if (pos > mSpec.Length())
1097 : return nullptr;
1098 657 : if (len < 0)
1099 : return nullptr;
1100 657 : if ((uint32_t)len > (mSpec.Length() - pos))
1101 : return nullptr;
1102 657 : if (!tail)
1103 : return nullptr;
1104 :
1105 657 : uint32_t tailLen = strlen(tail);
1106 :
1107 : // Check for int overflow for proposed length of combined string
1108 657 : if (UINT32_MAX - ((uint32_t)len + 1) < tailLen)
1109 : return nullptr;
1110 :
1111 657 : char *result = (char *) moz_xmalloc(len + tailLen + 1);
1112 0 : if (result) {
1113 0 : memcpy(result, mSpec.get() + pos, len);
1114 0 : memcpy(result + len, tail, tailLen);
1115 0 : result[len + tailLen] = '\0';
1116 : }
1117 : return result;
1118 : }
1119 :
1120 : nsresult
1121 0 : nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
1122 : {
1123 : nsresult rv;
1124 :
1125 0 : rv = stream->Read32(&seg.mPos);
1126 0 : if (NS_FAILED(rv)) return rv;
1127 :
1128 0 : rv = stream->Read32((uint32_t *) &seg.mLen);
1129 0 : if (NS_FAILED(rv)) return rv;
1130 :
1131 0 : return NS_OK;
1132 : }
1133 :
1134 : nsresult
1135 39 : nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
1136 : {
1137 : nsresult rv;
1138 :
1139 39 : rv = stream->Write32(seg.mPos);
1140 0 : if (NS_FAILED(rv)) return rv;
1141 :
1142 39 : rv = stream->Write32(uint32_t(seg.mLen));
1143 0 : if (NS_FAILED(rv)) return rv;
1144 :
1145 39 : return NS_OK;
1146 : }
1147 :
1148 : #define SHIFT_FROM(name, what) \
1149 : void \
1150 : nsStandardURL::name(int32_t diff) \
1151 : { \
1152 : if (!diff) return; \
1153 : if (what.mLen >= 0) { \
1154 : CheckedInt<int32_t> pos = what.mPos; \
1155 : pos += diff; \
1156 : MOZ_ASSERT(pos.isValid()); \
1157 : what.mPos = pos.value(); \
1158 : }
1159 :
1160 : #define SHIFT_FROM_NEXT(name, what, next) \
1161 : SHIFT_FROM(name, what) \
1162 : next(diff); \
1163 : }
1164 :
1165 : #define SHIFT_FROM_LAST(name, what) \
1166 : SHIFT_FROM(name, what) \
1167 : }
1168 :
1169 0 : SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername)
1170 0 : SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword)
1171 0 : SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost)
1172 0 : SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath)
1173 0 : SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath)
1174 0 : SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory)
1175 0 : SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename)
1176 0 : SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension)
1177 0 : SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery)
1178 0 : SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef)
1179 0 : SHIFT_FROM_LAST(ShiftFromRef, mRef)
1180 :
1181 : //----------------------------------------------------------------------------
1182 : // nsStandardURL::nsISupports
1183 : //----------------------------------------------------------------------------
1184 :
1185 65190 : NS_IMPL_ADDREF(nsStandardURL)
1186 0 : NS_IMPL_RELEASE(nsStandardURL)
1187 :
1188 20992 : NS_INTERFACE_MAP_BEGIN(nsStandardURL)
1189 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
1190 0 : NS_INTERFACE_MAP_ENTRY(nsIURI)
1191 0 : NS_INTERFACE_MAP_ENTRY(nsIURL)
1192 0 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
1193 0 : NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
1194 0 : NS_INTERFACE_MAP_ENTRY(nsISerializable)
1195 0 : NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
1196 0 : NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
1197 0 : NS_INTERFACE_MAP_ENTRY(nsISensitiveInfoHiddenURI)
1198 : // see nsStandardURL::Equals
1199 3381 : if (aIID.Equals(kThisImplCID))
1200 0 : foundInterface = static_cast<nsIURI *>(this);
1201 : else
1202 2098 : NS_INTERFACE_MAP_ENTRY(nsISizeOf)
1203 0 : NS_INTERFACE_MAP_END
1204 :
1205 : //----------------------------------------------------------------------------
1206 : // nsStandardURL::nsIURI
1207 : //----------------------------------------------------------------------------
1208 :
1209 : // result may contain unescaped UTF-8 characters
1210 : NS_IMETHODIMP
1211 8455 : nsStandardURL::GetSpec(nsACString &result)
1212 : {
1213 8455 : MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
1214 : "The spec should never be this long, we missed a check.");
1215 8456 : nsresult rv = NS_OK;
1216 0 : if (gPunycodeHost) {
1217 0 : result = mSpec;
1218 : } else { // XXX: This code path may be slow
1219 0 : rv = GetDisplaySpec(result);
1220 : }
1221 8456 : return rv;
1222 : }
1223 :
1224 : // result may contain unescaped UTF-8 characters
1225 : NS_IMETHODIMP
1226 0 : nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
1227 : {
1228 0 : nsresult rv = GetSpec(result);
1229 0 : if (NS_FAILED(rv)) {
1230 : return rv;
1231 : }
1232 0 : if (mPassword.mLen >= 0) {
1233 0 : result.ReplaceLiteral(mPassword.mPos, mPassword.mLen, "****");
1234 : }
1235 : return NS_OK;
1236 : }
1237 :
1238 : // result may contain unescaped UTF-8 characters
1239 : NS_IMETHODIMP
1240 120 : nsStandardURL::GetSpecIgnoringRef(nsACString &result)
1241 : {
1242 : // URI without ref is 0 to one char before ref
1243 120 : if (mRef.mLen < 0) {
1244 0 : return GetSpec(result);
1245 : }
1246 :
1247 0 : URLSegment noRef(0, mRef.mPos - 1);
1248 0 : result = Segment(noRef);
1249 :
1250 0 : MOZ_ASSERT(mCheckedIfHostA);
1251 0 : if (!gPunycodeHost && !mDisplayHost.IsEmpty()) {
1252 0 : result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
1253 : }
1254 :
1255 : return NS_OK;
1256 : }
1257 :
1258 : nsresult
1259 0 : nsStandardURL::CheckIfHostIsAscii()
1260 : {
1261 : nsresult rv;
1262 0 : if (mCheckedIfHostA) {
1263 : return NS_OK;
1264 : }
1265 :
1266 0 : mCheckedIfHostA = true;
1267 :
1268 0 : if (!gIDN) {
1269 : return NS_ERROR_NOT_INITIALIZED;
1270 : }
1271 :
1272 0 : nsAutoCString displayHost;
1273 : bool isAscii;
1274 0 : rv = gIDN->ConvertToDisplayIDN(Host(), &isAscii, displayHost);
1275 0 : if (NS_FAILED(rv)) {
1276 0 : mDisplayHost.Truncate();
1277 0 : mCheckedIfHostA = false;
1278 0 : return rv;
1279 : }
1280 :
1281 0 : if (!isAscii) {
1282 0 : mDisplayHost = displayHost;
1283 : }
1284 :
1285 : return NS_OK;
1286 : }
1287 :
1288 : NS_IMETHODIMP
1289 0 : nsStandardURL::GetDisplaySpec(nsACString &aUnicodeSpec)
1290 : {
1291 0 : aUnicodeSpec.Assign(mSpec);
1292 0 : MOZ_ASSERT(mCheckedIfHostA);
1293 0 : if (!mDisplayHost.IsEmpty()) {
1294 0 : aUnicodeSpec.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
1295 : }
1296 :
1297 0 : return NS_OK;
1298 : }
1299 :
1300 : NS_IMETHODIMP
1301 0 : nsStandardURL::GetDisplayHostPort(nsACString &aUnicodeHostPort)
1302 : {
1303 0 : nsAutoCString unicodeHostPort;
1304 :
1305 0 : nsresult rv = GetDisplayHost(unicodeHostPort);
1306 0 : if (NS_FAILED(rv)) {
1307 : return rv;
1308 : }
1309 :
1310 0 : if (StringBeginsWith(Hostport(), NS_LITERAL_CSTRING("["))) {
1311 0 : aUnicodeHostPort.AssignLiteral("[");
1312 0 : aUnicodeHostPort.Append(unicodeHostPort);
1313 0 : aUnicodeHostPort.AppendLiteral("]");
1314 : } else {
1315 0 : aUnicodeHostPort.Assign(unicodeHostPort);
1316 : }
1317 :
1318 0 : uint32_t pos = mHost.mPos + mHost.mLen;
1319 0 : if (pos < mPath.mPos)
1320 0 : aUnicodeHostPort += Substring(mSpec, pos, mPath.mPos - pos);
1321 :
1322 : return NS_OK;
1323 : }
1324 :
1325 : NS_IMETHODIMP
1326 0 : nsStandardURL::GetDisplayHost(nsACString &aUnicodeHost)
1327 : {
1328 0 : MOZ_ASSERT(mCheckedIfHostA);
1329 0 : if (mDisplayHost.IsEmpty()) {
1330 0 : return GetAsciiHost(aUnicodeHost);
1331 : }
1332 :
1333 0 : aUnicodeHost = mDisplayHost;
1334 0 : return NS_OK;
1335 : }
1336 :
1337 :
1338 : // result may contain unescaped UTF-8 characters
1339 : NS_IMETHODIMP
1340 0 : nsStandardURL::GetPrePath(nsACString &result)
1341 : {
1342 0 : result = Prepath();
1343 0 : MOZ_ASSERT(mCheckedIfHostA);
1344 0 : if (!gPunycodeHost && !mDisplayHost.IsEmpty()) {
1345 0 : result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
1346 : }
1347 0 : return NS_OK;
1348 : }
1349 :
1350 : // result may contain unescaped UTF-8 characters
1351 : NS_IMETHODIMP
1352 0 : nsStandardURL::GetDisplayPrePath(nsACString &result)
1353 : {
1354 0 : result = Prepath();
1355 0 : MOZ_ASSERT(mCheckedIfHostA);
1356 0 : if (!mDisplayHost.IsEmpty()) {
1357 0 : result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
1358 : }
1359 0 : return NS_OK;
1360 : }
1361 :
1362 : // result is strictly US-ASCII
1363 : NS_IMETHODIMP
1364 2478 : nsStandardURL::GetScheme(nsACString &result)
1365 : {
1366 7434 : result = Scheme();
1367 0 : return NS_OK;
1368 : }
1369 :
1370 : // result may contain unescaped UTF-8 characters
1371 : NS_IMETHODIMP
1372 25 : nsStandardURL::GetUserPass(nsACString &result)
1373 : {
1374 75 : result = Userpass();
1375 0 : return NS_OK;
1376 : }
1377 :
1378 : // result may contain unescaped UTF-8 characters
1379 : NS_IMETHODIMP
1380 6 : nsStandardURL::GetUsername(nsACString &result)
1381 : {
1382 18 : result = Username();
1383 0 : return NS_OK;
1384 : }
1385 :
1386 : // result may contain unescaped UTF-8 characters
1387 : NS_IMETHODIMP
1388 4 : nsStandardURL::GetPassword(nsACString &result)
1389 : {
1390 12 : result = Password();
1391 0 : return NS_OK;
1392 : }
1393 :
1394 : NS_IMETHODIMP
1395 675 : nsStandardURL::GetHostPort(nsACString &result)
1396 : {
1397 : nsresult rv;
1398 675 : if (gPunycodeHost) {
1399 0 : rv = GetAsciiHostPort(result);
1400 : } else {
1401 0 : rv = GetDisplayHostPort(result);
1402 : }
1403 675 : return rv;
1404 : }
1405 :
1406 : NS_IMETHODIMP
1407 244 : nsStandardURL::GetHost(nsACString &result)
1408 : {
1409 : nsresult rv;
1410 244 : if (gPunycodeHost) {
1411 0 : rv = GetAsciiHost(result);
1412 : } else {
1413 0 : rv = GetDisplayHost(result);
1414 : }
1415 244 : return rv;
1416 : }
1417 :
1418 : NS_IMETHODIMP
1419 39 : nsStandardURL::GetPort(int32_t *result)
1420 : {
1421 : // should never be more than 16 bit
1422 39 : MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
1423 0 : *result = mPort;
1424 0 : return NS_OK;
1425 : }
1426 :
1427 : // result may contain unescaped UTF-8 characters
1428 : NS_IMETHODIMP
1429 1858 : nsStandardURL::GetPathQueryRef(nsACString &result)
1430 : {
1431 5574 : result = Path();
1432 0 : return NS_OK;
1433 : }
1434 :
1435 : // result is ASCII
1436 : NS_IMETHODIMP
1437 88 : nsStandardURL::GetAsciiSpec(nsACString &result)
1438 : {
1439 176 : result = mSpec;
1440 0 : return NS_OK;
1441 : }
1442 :
1443 : // result is ASCII
1444 : NS_IMETHODIMP
1445 779 : nsStandardURL::GetAsciiHostPort(nsACString &result)
1446 : {
1447 2337 : result = Hostport();
1448 0 : return NS_OK;
1449 : }
1450 :
1451 : // result is ASCII
1452 : NS_IMETHODIMP
1453 1034 : nsStandardURL::GetAsciiHost(nsACString &result)
1454 : {
1455 3102 : result = Host();
1456 0 : return NS_OK;
1457 : }
1458 :
1459 : static bool
1460 3756 : IsSpecialProtocol(const nsACString &input)
1461 : {
1462 7512 : nsACString::const_iterator start, end;
1463 0 : input.BeginReading(start);
1464 0 : nsACString::const_iterator iterator(start);
1465 0 : input.EndReading(end);
1466 :
1467 36162 : while (iterator != end && *iterator != ':') {
1468 0 : iterator++;
1469 : }
1470 :
1471 11268 : nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get()));
1472 :
1473 7507 : return protocol.LowerCaseEqualsLiteral("http") ||
1474 0 : protocol.LowerCaseEqualsLiteral("https") ||
1475 0 : protocol.LowerCaseEqualsLiteral("ftp") ||
1476 0 : protocol.LowerCaseEqualsLiteral("ws") ||
1477 0 : protocol.LowerCaseEqualsLiteral("wss") ||
1478 0 : protocol.LowerCaseEqualsLiteral("file") ||
1479 0 : protocol.LowerCaseEqualsLiteral("gopher");
1480 : }
1481 :
1482 : nsresult
1483 3 : nsStandardURL::SetSpecInternal(const nsACString &input)
1484 : {
1485 3 : return SetSpecWithEncoding(input, nullptr);
1486 : }
1487 :
1488 : nsresult
1489 2442 : nsStandardURL::SetSpecWithEncoding(const nsACString &input,
1490 : const Encoding* encoding)
1491 : {
1492 4884 : const nsPromiseFlatCString &flat = PromiseFlatCString(input);
1493 0 : LOG(("nsStandardURL::SetSpec [spec=%s]\n", flat.get()));
1494 :
1495 2442 : if (input.Length() > (uint32_t) net_GetURLMaxLength()) {
1496 : return NS_ERROR_MALFORMED_URI;
1497 : }
1498 :
1499 : // filter out unexpected chars "\r\n\t" if necessary
1500 2442 : nsAutoCString filteredURI;
1501 0 : net_FilterURIString(flat, filteredURI);
1502 :
1503 2442 : if (filteredURI.Length() == 0) {
1504 : return NS_ERROR_MALFORMED_URI;
1505 : }
1506 :
1507 : // Make a backup of the curent URL
1508 4884 : nsStandardURL prevURL(false,false);
1509 0 : prevURL.CopyMembers(this, eHonorRef, EmptyCString());
1510 0 : Clear();
1511 :
1512 2442 : if (IsSpecialProtocol(filteredURI)) {
1513 : // Bug 652186: Replace all backslashes with slashes when parsing paths
1514 : // Stop when we reach the query or the hash.
1515 706 : nsAutoCString::iterator start;
1516 0 : nsAutoCString::iterator end;
1517 0 : filteredURI.BeginWriting(start);
1518 0 : filteredURI.EndWriting(end);
1519 0 : while (start != end) {
1520 0 : if (*start == '?' || *start == '#') {
1521 : break;
1522 : }
1523 47319 : if (*start == '\\') {
1524 0 : *start = '/';
1525 : }
1526 94638 : start++;
1527 : }
1528 : }
1529 :
1530 2442 : const char *spec = filteredURI.get();
1531 0 : int32_t specLength = filteredURI.Length();
1532 :
1533 : // parse the given URL...
1534 2442 : nsresult rv = ParseURL(spec, specLength);
1535 0 : if (NS_SUCCEEDED(rv)) {
1536 : // finally, use the URLSegment member variables to build a normalized
1537 : // copy of |spec|
1538 2442 : rv = BuildNormalizedSpec(spec, encoding);
1539 : }
1540 :
1541 : // Make sure that a URLTYPE_AUTHORITY has a non-empty hostname.
1542 2442 : if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
1543 0 : rv = NS_ERROR_MALFORMED_URI;
1544 : }
1545 :
1546 2442 : if (NS_FAILED(rv)) {
1547 0 : Clear();
1548 : // If parsing the spec has failed, restore the old URL
1549 : // so we don't end up with an empty URL.
1550 0 : CopyMembers(&prevURL, eHonorRef, EmptyCString());
1551 0 : return rv;
1552 : }
1553 :
1554 2442 : if (LOG_ENABLED()) {
1555 0 : LOG((" spec = %s\n", mSpec.get()));
1556 0 : LOG((" port = %d\n", mPort));
1557 0 : LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen));
1558 0 : LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
1559 0 : LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen));
1560 0 : LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen));
1561 0 : LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen));
1562 0 : LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen));
1563 0 : LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen));
1564 0 : LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
1565 0 : LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen));
1566 0 : LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
1567 0 : LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen));
1568 0 : LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen));
1569 : }
1570 :
1571 : return rv;
1572 : }
1573 :
1574 : nsresult
1575 0 : nsStandardURL::SetScheme(const nsACString &input)
1576 : {
1577 0 : const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
1578 :
1579 0 : LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
1580 :
1581 0 : if (scheme.IsEmpty()) {
1582 0 : NS_WARNING("cannot remove the scheme from an url");
1583 0 : return NS_ERROR_UNEXPECTED;
1584 : }
1585 0 : if (mScheme.mLen < 0) {
1586 0 : NS_WARNING("uninitialized");
1587 0 : return NS_ERROR_NOT_INITIALIZED;
1588 : }
1589 :
1590 0 : if (!net_IsValidScheme(scheme)) {
1591 0 : NS_WARNING("the given url scheme contains invalid characters");
1592 0 : return NS_ERROR_UNEXPECTED;
1593 : }
1594 :
1595 0 : if (mSpec.Length() + input.Length() - Scheme().Length() > (uint32_t) net_GetURLMaxLength()) {
1596 : return NS_ERROR_MALFORMED_URI;
1597 : }
1598 :
1599 0 : InvalidateCache();
1600 :
1601 0 : int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
1602 :
1603 0 : if (shift) {
1604 0 : mScheme.mLen = scheme.Length();
1605 0 : ShiftFromAuthority(shift);
1606 : }
1607 :
1608 : // ensure new scheme is lowercase
1609 : //
1610 : // XXX the string code unfortunately doesn't provide a ToLowerCase
1611 : // that operates on a substring.
1612 0 : net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
1613 0 : return NS_OK;
1614 : }
1615 :
1616 : nsresult
1617 0 : nsStandardURL::SetUserPass(const nsACString &input)
1618 : {
1619 0 : const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
1620 :
1621 0 : LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
1622 :
1623 0 : if (mURLType == URLTYPE_NO_AUTHORITY) {
1624 0 : if (userpass.IsEmpty())
1625 : return NS_OK;
1626 0 : NS_WARNING("cannot set user:pass on no-auth url");
1627 0 : return NS_ERROR_UNEXPECTED;
1628 : }
1629 0 : if (mAuthority.mLen < 0) {
1630 0 : NS_WARNING("uninitialized");
1631 0 : return NS_ERROR_NOT_INITIALIZED;
1632 : }
1633 :
1634 0 : if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) {
1635 : return NS_ERROR_MALFORMED_URI;
1636 : }
1637 :
1638 0 : InvalidateCache();
1639 :
1640 0 : if (userpass.IsEmpty()) {
1641 : // remove user:pass
1642 0 : if (mUsername.mLen > 0) {
1643 0 : if (mPassword.mLen > 0)
1644 0 : mUsername.mLen += (mPassword.mLen + 1);
1645 0 : mUsername.mLen++;
1646 0 : mSpec.Cut(mUsername.mPos, mUsername.mLen);
1647 0 : mAuthority.mLen -= mUsername.mLen;
1648 0 : ShiftFromHost(-mUsername.mLen);
1649 0 : mUsername.mLen = -1;
1650 0 : mPassword.mLen = -1;
1651 : }
1652 :
1653 : return NS_OK;
1654 : }
1655 :
1656 0 : NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
1657 :
1658 : nsresult rv;
1659 : uint32_t usernamePos, passwordPos;
1660 : int32_t usernameLen, passwordLen;
1661 :
1662 0 : rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
1663 : &usernamePos, &usernameLen,
1664 0 : &passwordPos, &passwordLen);
1665 0 : if (NS_FAILED(rv)) return rv;
1666 :
1667 : // build new user:pass in |buf|
1668 0 : nsAutoCString buf;
1669 0 : if (usernameLen > 0) {
1670 0 : nsSegmentEncoder encoder;
1671 : bool ignoredOut;
1672 0 : usernameLen = encoder.EncodeSegmentCount(userpass.get(),
1673 0 : URLSegment(usernamePos,
1674 : usernameLen),
1675 : esc_Username | esc_AlwaysCopy,
1676 : buf, ignoredOut);
1677 0 : if (passwordLen > 0) {
1678 0 : buf.Append(':');
1679 0 : passwordLen = encoder.EncodeSegmentCount(userpass.get(),
1680 0 : URLSegment(passwordPos,
1681 : passwordLen),
1682 : esc_Password |
1683 : esc_AlwaysCopy, buf,
1684 : ignoredOut);
1685 : } else {
1686 0 : passwordLen = -1;
1687 : }
1688 0 : if (mUsername.mLen < 0)
1689 0 : buf.Append('@');
1690 : }
1691 :
1692 0 : uint32_t shift = 0;
1693 :
1694 0 : if (mUsername.mLen < 0) {
1695 : // no existing user:pass
1696 0 : if (!buf.IsEmpty()) {
1697 0 : mSpec.Insert(buf, mHost.mPos);
1698 0 : mUsername.mPos = mHost.mPos;
1699 0 : shift = buf.Length();
1700 : }
1701 : }
1702 : else {
1703 : // replace existing user:pass
1704 0 : uint32_t userpassLen = mUsername.mLen;
1705 0 : if (mPassword.mLen >= 0)
1706 0 : userpassLen += (mPassword.mLen + 1);
1707 0 : mSpec.Replace(mUsername.mPos, userpassLen, buf);
1708 0 : shift = buf.Length() - userpassLen;
1709 : }
1710 0 : if (shift) {
1711 0 : ShiftFromHost(shift);
1712 0 : mAuthority.mLen += shift;
1713 : }
1714 : // update positions and lengths
1715 0 : mUsername.mLen = usernameLen;
1716 0 : mPassword.mLen = passwordLen;
1717 0 : if (passwordLen > 0) {
1718 0 : mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
1719 : }
1720 :
1721 0 : return NS_OK;
1722 : }
1723 :
1724 : nsresult
1725 0 : nsStandardURL::SetUsername(const nsACString &input)
1726 : {
1727 0 : const nsPromiseFlatCString &username = PromiseFlatCString(input);
1728 :
1729 0 : LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
1730 :
1731 0 : if (mURLType == URLTYPE_NO_AUTHORITY) {
1732 0 : if (username.IsEmpty())
1733 : return NS_OK;
1734 0 : NS_WARNING("cannot set username on no-auth url");
1735 0 : return NS_ERROR_UNEXPECTED;
1736 : }
1737 :
1738 0 : if (username.IsEmpty())
1739 0 : return SetUserPass(username);
1740 :
1741 0 : if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) {
1742 : return NS_ERROR_MALFORMED_URI;
1743 : }
1744 :
1745 0 : InvalidateCache();
1746 :
1747 : // escape username if necessary
1748 0 : nsAutoCString buf;
1749 0 : nsSegmentEncoder encoder;
1750 : const nsACString &escUsername =
1751 0 : encoder.EncodeSegment(username, esc_Username, buf);
1752 :
1753 : int32_t shift;
1754 :
1755 0 : if (mUsername.mLen < 0) {
1756 0 : mUsername.mPos = mAuthority.mPos;
1757 0 : mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
1758 0 : shift = escUsername.Length() + 1;
1759 : }
1760 : else
1761 0 : shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
1762 :
1763 0 : if (shift) {
1764 0 : mUsername.mLen = escUsername.Length();
1765 0 : mAuthority.mLen += shift;
1766 0 : ShiftFromPassword(shift);
1767 : }
1768 :
1769 0 : return NS_OK;
1770 : }
1771 :
1772 : nsresult
1773 0 : nsStandardURL::SetPassword(const nsACString &input)
1774 : {
1775 0 : const nsPromiseFlatCString &password = PromiseFlatCString(input);
1776 :
1777 0 : auto clearedPassword = MakeScopeExit([&password, this]() {
1778 : // Check that if this method is called with the empty string then the
1779 : // password is definitely cleared when exiting this method.
1780 0 : if (password.IsEmpty()) {
1781 0 : MOZ_DIAGNOSTIC_ASSERT(this->Password().IsEmpty());
1782 : }
1783 0 : Unused << this; // silence compiler -Wunused-lambda-capture
1784 0 : });
1785 :
1786 0 : LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
1787 :
1788 0 : if (mURLType == URLTYPE_NO_AUTHORITY) {
1789 0 : if (password.IsEmpty())
1790 : return NS_OK;
1791 0 : NS_WARNING("cannot set password on no-auth url");
1792 0 : return NS_ERROR_UNEXPECTED;
1793 : }
1794 0 : if (mUsername.mLen <= 0) {
1795 0 : if (password.IsEmpty()) {
1796 0 : MOZ_DIAGNOSTIC_ASSERT(Password().IsEmpty());
1797 : return NS_OK;
1798 : }
1799 0 : NS_WARNING("cannot set password without existing username");
1800 0 : return NS_ERROR_FAILURE;
1801 : }
1802 :
1803 0 : if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) {
1804 : return NS_ERROR_MALFORMED_URI;
1805 : }
1806 :
1807 0 : InvalidateCache();
1808 :
1809 0 : if (password.IsEmpty()) {
1810 0 : if (mPassword.mLen >= 0) {
1811 : // cut(":password")
1812 0 : mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
1813 0 : ShiftFromHost(-(mPassword.mLen + 1));
1814 0 : mAuthority.mLen -= (mPassword.mLen + 1);
1815 0 : mPassword.mLen = -1;
1816 : }
1817 : return NS_OK;
1818 : }
1819 :
1820 : // escape password if necessary
1821 0 : nsAutoCString buf;
1822 0 : nsSegmentEncoder encoder;
1823 : const nsACString &escPassword =
1824 0 : encoder.EncodeSegment(password, esc_Password, buf);
1825 :
1826 : int32_t shift;
1827 :
1828 0 : if (mPassword.mLen < 0) {
1829 0 : mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
1830 0 : mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
1831 0 : shift = escPassword.Length() + 1;
1832 : }
1833 : else
1834 0 : shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
1835 :
1836 0 : if (shift) {
1837 0 : mPassword.mLen = escPassword.Length();
1838 0 : mAuthority.mLen += shift;
1839 0 : ShiftFromHost(shift);
1840 : }
1841 0 : return NS_OK;
1842 : }
1843 :
1844 : void
1845 0 : nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
1846 : nsACString::const_iterator& aEnd)
1847 : {
1848 0 : for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
1849 0 : nsACString::const_iterator c(aStart);
1850 0 : if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
1851 0 : aEnd = c;
1852 : }
1853 : }
1854 0 : }
1855 :
1856 : // If aValue only has a host part and no port number, the port
1857 : // will not be reset!!!
1858 : nsresult
1859 0 : nsStandardURL::SetHostPort(const nsACString &aValue)
1860 : {
1861 : // We cannot simply call nsIURI::SetHost because that would treat the name as
1862 : // an IPv6 address (like http:://[server:443]/). We also cannot call
1863 : // nsIURI::SetHostPort because that isn't implemented. Sadfaces.
1864 :
1865 0 : nsACString::const_iterator start, end;
1866 0 : aValue.BeginReading(start);
1867 0 : aValue.EndReading(end);
1868 0 : nsACString::const_iterator iter(start);
1869 0 : bool isIPv6 = false;
1870 :
1871 0 : FindHostLimit(start, end);
1872 :
1873 0 : if (*start == '[') { // IPv6 address
1874 0 : if (!FindCharInReadable(']', iter, end)) {
1875 : // the ] character is missing
1876 : return NS_ERROR_MALFORMED_URI;
1877 : }
1878 : // iter now at the ']' character
1879 : isIPv6 = true;
1880 : } else {
1881 0 : nsACString::const_iterator iter2(start);
1882 0 : if (FindCharInReadable(']', iter2, end)) {
1883 : // if the first char isn't [ then there should be no ] character
1884 0 : return NS_ERROR_MALFORMED_URI;
1885 : }
1886 : }
1887 :
1888 0 : FindCharInReadable(':', iter, end);
1889 :
1890 0 : if (!isIPv6 && iter != end) {
1891 0 : nsACString::const_iterator iter2(iter);
1892 0 : iter2++; // Skip over the first ':' character
1893 0 : if (FindCharInReadable(':', iter2, end)) {
1894 : // If there is more than one ':' character it suggests an IPv6
1895 : // The format should be [2001::1]:80 where the port is optional
1896 0 : return NS_ERROR_MALFORMED_URI;
1897 : }
1898 : }
1899 :
1900 0 : nsresult rv = SetHost(Substring(start, iter));
1901 0 : NS_ENSURE_SUCCESS(rv, rv);
1902 :
1903 0 : if (iter == end) {
1904 : // does not end in colon
1905 : return NS_OK;
1906 : }
1907 :
1908 0 : iter++; // advance over the colon
1909 0 : if (iter == end) {
1910 : // port number is missing
1911 : return NS_OK;
1912 : }
1913 :
1914 0 : nsCString portStr(Substring(iter, end));
1915 0 : int32_t port = portStr.ToInteger(&rv);
1916 0 : if (NS_FAILED(rv)) {
1917 : // Failure parsing the port number
1918 : return NS_OK;
1919 : }
1920 :
1921 0 : Unused << SetPort(port);
1922 0 : return NS_OK;
1923 : }
1924 :
1925 : nsresult
1926 0 : nsStandardURL::SetHost(const nsACString &input)
1927 : {
1928 0 : const nsPromiseFlatCString &hostname = PromiseFlatCString(input);
1929 :
1930 0 : nsACString::const_iterator start, end;
1931 0 : hostname.BeginReading(start);
1932 0 : hostname.EndReading(end);
1933 :
1934 0 : FindHostLimit(start, end);
1935 :
1936 0 : const nsCString unescapedHost(Substring(start, end));
1937 : // Do percent decoding on the the input.
1938 0 : nsAutoCString flat;
1939 0 : NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(),
1940 0 : esc_AlwaysCopy | esc_Host, flat);
1941 0 : const char *host = flat.get();
1942 :
1943 0 : LOG(("nsStandardURL::SetHost [host=%s]\n", host));
1944 :
1945 0 : if (mURLType == URLTYPE_NO_AUTHORITY) {
1946 0 : if (flat.IsEmpty())
1947 : return NS_OK;
1948 0 : NS_WARNING("cannot set host on no-auth url");
1949 0 : return NS_ERROR_UNEXPECTED;
1950 : }
1951 0 : if (flat.IsEmpty()) {
1952 : // Setting an empty hostname is not allowed for
1953 : // URLTYPE_STANDARD and URLTYPE_AUTHORITY.
1954 : return NS_ERROR_UNEXPECTED;
1955 : }
1956 :
1957 0 : if (strlen(host) < flat.Length())
1958 : return NS_ERROR_MALFORMED_URI; // found embedded null
1959 :
1960 : // For consistency with SetSpec/nsURLParsers, don't allow spaces
1961 : // in the hostname.
1962 0 : if (strchr(host, ' '))
1963 : return NS_ERROR_MALFORMED_URI;
1964 :
1965 0 : if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) {
1966 : return NS_ERROR_MALFORMED_URI;
1967 : }
1968 :
1969 0 : InvalidateCache();
1970 :
1971 : uint32_t len;
1972 0 : nsAutoCString hostBuf;
1973 0 : nsresult rv = NormalizeIDN(flat, hostBuf);
1974 0 : if (NS_FAILED(rv)) {
1975 : return rv;
1976 : }
1977 :
1978 0 : if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) {
1979 0 : nsAutoCString ipString;
1980 0 : if (hostBuf.Length() > 0 &&
1981 0 : hostBuf.First() == '[' && hostBuf.Last() == ']' &&
1982 0 : ValidIPv6orHostname(hostBuf.get(), hostBuf.Length())) {
1983 0 : rv = (nsresult) rusturl_parse_ipv6addr(&hostBuf, &ipString);
1984 0 : if (NS_FAILED(rv)) {
1985 0 : return rv;
1986 : }
1987 : hostBuf = ipString;
1988 0 : } else if (NS_SUCCEEDED(NormalizeIPv4(hostBuf, ipString))) {
1989 : hostBuf = ipString;
1990 : }
1991 : }
1992 :
1993 : // NormalizeIDN always copies if the call was successful
1994 0 : host = hostBuf.get();
1995 0 : len = hostBuf.Length();
1996 :
1997 0 : if (!ValidIPv6orHostname(host, len)) {
1998 : return NS_ERROR_MALFORMED_URI;
1999 : }
2000 :
2001 0 : if (mHost.mLen < 0) {
2002 0 : int port_length = 0;
2003 0 : if (mPort != -1) {
2004 0 : nsAutoCString buf;
2005 0 : buf.Assign(':');
2006 0 : buf.AppendInt(mPort);
2007 0 : port_length = buf.Length();
2008 : }
2009 0 : if (mAuthority.mLen > 0) {
2010 0 : mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
2011 0 : mHost.mLen = 0;
2012 0 : } else if (mScheme.mLen > 0) {
2013 0 : mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
2014 0 : mHost.mLen = 0;
2015 : }
2016 : }
2017 :
2018 0 : int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
2019 :
2020 0 : if (shift) {
2021 0 : mHost.mLen = len;
2022 0 : mAuthority.mLen += shift;
2023 0 : ShiftFromPath(shift);
2024 : }
2025 :
2026 : // Now canonicalize the host to lowercase
2027 0 : net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
2028 0 : return NS_OK;
2029 : }
2030 :
2031 : nsresult
2032 0 : nsStandardURL::SetPort(int32_t port)
2033 : {
2034 0 : LOG(("nsStandardURL::SetPort [port=%d]\n", port));
2035 :
2036 0 : if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
2037 : return NS_OK;
2038 :
2039 : // ports must be >= 0 and 16 bit
2040 : // -1 == use default
2041 0 : if (port < -1 || port > std::numeric_limits<uint16_t>::max())
2042 : return NS_ERROR_MALFORMED_URI;
2043 :
2044 0 : if (mURLType == URLTYPE_NO_AUTHORITY) {
2045 0 : NS_WARNING("cannot set port on no-auth url");
2046 0 : return NS_ERROR_UNEXPECTED;
2047 : }
2048 :
2049 0 : InvalidateCache();
2050 0 : if (port == mDefaultPort) {
2051 0 : port = -1;
2052 : }
2053 :
2054 0 : ReplacePortInSpec(port);
2055 :
2056 0 : mPort = port;
2057 0 : return NS_OK;
2058 : }
2059 :
2060 : /**
2061 : * Replaces the existing port in mSpec with aNewPort.
2062 : *
2063 : * The caller is responsible for:
2064 : * - Calling InvalidateCache (since our mSpec is changing).
2065 : * - Checking whether aNewPort is mDefaultPort (in which case the
2066 : * caller should pass aNewPort=-1).
2067 : */
2068 : void
2069 0 : nsStandardURL::ReplacePortInSpec(int32_t aNewPort)
2070 : {
2071 0 : NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1,
2072 : "Caller should check its passed-in value and pass -1 instead of "
2073 : "mDefaultPort, to avoid encoding default port into mSpec");
2074 :
2075 : // Create the (possibly empty) string that we're planning to replace:
2076 0 : nsAutoCString buf;
2077 0 : if (mPort != -1) {
2078 0 : buf.Assign(':');
2079 0 : buf.AppendInt(mPort);
2080 : }
2081 : // Find the position & length of that string:
2082 0 : const uint32_t replacedLen = buf.Length();
2083 : const uint32_t replacedStart =
2084 0 : mAuthority.mPos + mAuthority.mLen - replacedLen;
2085 :
2086 : // Create the (possibly empty) replacement string:
2087 0 : if (aNewPort == -1) {
2088 0 : buf.Truncate();
2089 : } else {
2090 0 : buf.Assign(':');
2091 0 : buf.AppendInt(aNewPort);
2092 : }
2093 : // Perform the replacement:
2094 0 : mSpec.Replace(replacedStart, replacedLen, buf);
2095 :
2096 : // Bookkeeping to reflect the new length:
2097 0 : int32_t shift = buf.Length() - replacedLen;
2098 0 : mAuthority.mLen += shift;
2099 0 : ShiftFromPath(shift);
2100 0 : }
2101 :
2102 : nsresult
2103 3 : nsStandardURL::SetPathQueryRef(const nsACString &input)
2104 : {
2105 6 : const nsPromiseFlatCString &path = PromiseFlatCString(input);
2106 0 : LOG(("nsStandardURL::SetPathQueryRef [path=%s]\n", path.get()));
2107 :
2108 3 : InvalidateCache();
2109 :
2110 3 : if (!path.IsEmpty()) {
2111 0 : nsAutoCString spec;
2112 :
2113 3 : spec.Assign(mSpec.get(), mPath.mPos);
2114 0 : if (path.First() != '/')
2115 0 : spec.Append('/');
2116 0 : spec.Append(path);
2117 :
2118 3 : return SetSpecInternal(spec);
2119 : }
2120 0 : if (mPath.mLen >= 1) {
2121 0 : mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
2122 : // these contain only a '/'
2123 0 : mPath.mLen = 1;
2124 0 : mDirectory.mLen = 1;
2125 0 : mFilepath.mLen = 1;
2126 : // these are no longer defined
2127 0 : mBasename.mLen = -1;
2128 0 : mExtension.mLen = -1;
2129 0 : mQuery.mLen = -1;
2130 0 : mRef.mLen = -1;
2131 : }
2132 : return NS_OK;
2133 : }
2134 :
2135 : // When updating this also update SubstitutingURL::Mutator
2136 : // Queries this list of interfaces. If none match, it queries mURI.
2137 78628 : NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsStandardURL::Mutator,
2138 : nsIURISetters,
2139 : nsIURIMutator,
2140 : nsIStandardURLMutator,
2141 : nsIURLMutator,
2142 : nsIFileURLMutator,
2143 : nsISerializable)
2144 :
2145 : NS_IMETHODIMP
2146 87 : nsStandardURL::Mutate(nsIURIMutator** aMutator)
2147 : {
2148 174 : RefPtr<nsStandardURL::Mutator> mutator = new nsStandardURL::Mutator();
2149 0 : nsresult rv = mutator->InitFromURI(this);
2150 0 : if (NS_FAILED(rv)) {
2151 : return rv;
2152 : }
2153 87 : mutator.forget(aMutator);
2154 0 : return NS_OK;
2155 : }
2156 :
2157 : NS_IMETHODIMP
2158 1254 : nsStandardURL::Equals(nsIURI *unknownOther, bool *result)
2159 : {
2160 1254 : return EqualsInternal(unknownOther, eHonorRef, result);
2161 : }
2162 :
2163 : NS_IMETHODIMP
2164 35 : nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result)
2165 : {
2166 35 : return EqualsInternal(unknownOther, eIgnoreRef, result);
2167 : }
2168 :
2169 : nsresult
2170 1289 : nsStandardURL::EqualsInternal(nsIURI *unknownOther,
2171 : nsStandardURL::RefHandlingEnum refHandlingMode,
2172 : bool *result)
2173 : {
2174 1289 : NS_ENSURE_ARG_POINTER(unknownOther);
2175 0 : MOZ_ASSERT(result, "null pointer");
2176 :
2177 2578 : RefPtr<nsStandardURL> other;
2178 0 : nsresult rv = unknownOther->QueryInterface(kThisImplCID,
2179 0 : getter_AddRefs(other));
2180 0 : if (NS_FAILED(rv)) {
2181 0 : *result = false;
2182 0 : return NS_OK;
2183 : }
2184 :
2185 : // First, check whether one URIs is an nsIFileURL while the other
2186 : // is not. If that's the case, they're different.
2187 1283 : if (mSupportsFileURL != other->mSupportsFileURL) {
2188 0 : *result = false;
2189 0 : return NS_OK;
2190 : }
2191 :
2192 : // Next check parts of a URI that, if different, automatically make the
2193 : // URIs different
2194 3846 : if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
2195 : // Check for host manually, since conversion to file will
2196 : // ignore the host!
2197 2507 : !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
2198 0 : !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
2199 0 : !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
2200 0 : !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
2201 0 : Port() != other->Port()) {
2202 : // No need to compare files or other URI parts -- these are different
2203 : // beasties
2204 57 : *result = false;
2205 0 : return NS_OK;
2206 : }
2207 :
2208 2418 : if (refHandlingMode == eHonorRef &&
2209 0 : !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
2210 0 : *result = false;
2211 0 : return NS_OK;
2212 : }
2213 :
2214 : // Then check for exact identity of URIs. If we have it, they're equal
2215 3429 : if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
2216 0 : SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
2217 0 : SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
2218 0 : *result = true;
2219 0 : return NS_OK;
2220 : }
2221 :
2222 : // At this point, the URIs are not identical, but they only differ in the
2223 : // directory/filename/extension. If these are file URLs, then get the
2224 : // corresponding file objects and compare those, since two filenames that
2225 : // differ, eg, only in case could still be equal.
2226 38 : if (mSupportsFileURL) {
2227 : // Assume not equal for failure cases... but failures in GetFile are
2228 : // really failures, more or less, so propagate them to caller.
2229 15 : *result = false;
2230 :
2231 15 : rv = EnsureFile();
2232 0 : nsresult rv2 = other->EnsureFile();
2233 : // special case for resource:// urls that don't resolve to files
2234 15 : if (rv == NS_ERROR_NO_INTERFACE && rv == rv2)
2235 : return NS_OK;
2236 :
2237 8 : if (NS_FAILED(rv)) {
2238 0 : LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
2239 : this, mSpec.get()));
2240 : return rv;
2241 : }
2242 0 : NS_ASSERTION(mFile, "EnsureFile() lied!");
2243 0 : rv = rv2;
2244 0 : if (NS_FAILED(rv)) {
2245 0 : LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
2246 : other.get(), other->mSpec.get()));
2247 : return rv;
2248 : }
2249 0 : NS_ASSERTION(other->mFile, "EnsureFile() lied!");
2250 0 : return mFile->Equals(other->mFile, result);
2251 : }
2252 :
2253 : // The URLs are not identical, and they do not correspond to the
2254 : // same file, so they are different.
2255 23 : *result = false;
2256 :
2257 23 : return NS_OK;
2258 : }
2259 :
2260 : NS_IMETHODIMP
2261 5597 : nsStandardURL::SchemeIs(const char *scheme, bool *result)
2262 : {
2263 5597 : MOZ_ASSERT(result, "null pointer");
2264 :
2265 5597 : *result = SegmentIs(mScheme, scheme);
2266 0 : return NS_OK;
2267 : }
2268 :
2269 : /* virtual */ nsStandardURL*
2270 899 : nsStandardURL::StartClone()
2271 : {
2272 899 : nsStandardURL *clone = new nsStandardURL();
2273 0 : return clone;
2274 : }
2275 :
2276 : NS_IMETHODIMP
2277 451 : nsStandardURL::Clone(nsIURI **result)
2278 : {
2279 451 : return CloneInternal(eHonorRef, EmptyCString(), result);
2280 : }
2281 :
2282 :
2283 : NS_IMETHODIMP
2284 462 : nsStandardURL::CloneIgnoringRef(nsIURI **result)
2285 : {
2286 462 : return CloneInternal(eIgnoreRef, EmptyCString(), result);
2287 : }
2288 :
2289 : NS_IMETHODIMP
2290 0 : nsStandardURL::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
2291 : {
2292 0 : return CloneInternal(eReplaceRef, newRef, result);
2293 : }
2294 :
2295 : nsresult
2296 913 : nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
2297 : const nsACString& newRef,
2298 : nsIURI **result)
2299 :
2300 : {
2301 1826 : RefPtr<nsStandardURL> clone = StartClone();
2302 0 : if (!clone)
2303 : return NS_ERROR_OUT_OF_MEMORY;
2304 :
2305 : // Copy local members into clone.
2306 : // Also copies the cached members mFile, mDisplayHost
2307 913 : clone->CopyMembers(this, refHandlingMode, newRef, true);
2308 :
2309 913 : clone.forget(result);
2310 0 : return NS_OK;
2311 : }
2312 :
2313 3355 : nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
2314 : nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
2315 : bool copyCached)
2316 : {
2317 6710 : mSpec = source->mSpec;
2318 0 : mDefaultPort = source->mDefaultPort;
2319 0 : mPort = source->mPort;
2320 0 : mScheme = source->mScheme;
2321 0 : mAuthority = source->mAuthority;
2322 0 : mUsername = source->mUsername;
2323 0 : mPassword = source->mPassword;
2324 0 : mHost = source->mHost;
2325 0 : mPath = source->mPath;
2326 0 : mFilepath = source->mFilepath;
2327 0 : mDirectory = source->mDirectory;
2328 0 : mBasename = source->mBasename;
2329 0 : mExtension = source->mExtension;
2330 0 : mQuery = source->mQuery;
2331 0 : mRef = source->mRef;
2332 0 : mURLType = source->mURLType;
2333 0 : mParser = source->mParser;
2334 0 : mSupportsFileURL = source->mSupportsFileURL;
2335 0 : mCheckedIfHostA = source->mCheckedIfHostA;
2336 0 : mDisplayHost = source->mDisplayHost;
2337 :
2338 3355 : if (copyCached) {
2339 0 : mFile = source->mFile;
2340 : } else {
2341 : InvalidateCache(true);
2342 : }
2343 :
2344 3355 : if (refHandlingMode == eIgnoreRef) {
2345 0 : SetRef(EmptyCString());
2346 0 : } else if (refHandlingMode == eReplaceRef) {
2347 0 : SetRef(newRef);
2348 : }
2349 :
2350 :
2351 3355 : return NS_OK;
2352 : }
2353 :
2354 : NS_IMETHODIMP
2355 657 : nsStandardURL::Resolve(const nsACString &in, nsACString &out)
2356 : {
2357 1314 : const nsPromiseFlatCString &flat = PromiseFlatCString(in);
2358 : // filter out unexpected chars "\r\n\t" if necessary
2359 1314 : nsAutoCString buf;
2360 0 : net_FilterURIString(flat, buf);
2361 :
2362 657 : const char *relpath = buf.get();
2363 0 : int32_t relpathLen = buf.Length();
2364 :
2365 657 : char *result = nullptr;
2366 :
2367 657 : LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
2368 : this, mSpec.get(), relpath));
2369 :
2370 1314 : NS_ASSERTION(mParser, "no parser: unitialized");
2371 :
2372 : // NOTE: there is no need for this function to produce normalized
2373 : // output. normalization will occur when the result is used to
2374 : // initialize a nsStandardURL object.
2375 :
2376 657 : if (mScheme.mLen < 0) {
2377 0 : NS_WARNING("unable to Resolve URL: this URL not initialized");
2378 0 : return NS_ERROR_NOT_INITIALIZED;
2379 : }
2380 :
2381 : nsresult rv;
2382 657 : URLSegment scheme;
2383 0 : char *resultPath = nullptr;
2384 0 : bool relative = false;
2385 0 : uint32_t offset = 0;
2386 0 : netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
2387 :
2388 : // relative urls should never contain a host, so we always want to use
2389 : // the noauth url parser.
2390 : // use it to extract a possible scheme
2391 657 : rv = mParser->ParseURL(relpath,
2392 : relpathLen,
2393 : &scheme.mPos, &scheme.mLen,
2394 : nullptr, nullptr,
2395 657 : nullptr, nullptr);
2396 :
2397 : // if the parser fails (for example because there is no valid scheme)
2398 : // reset the scheme and assume a relative url
2399 657 : if (NS_FAILED(rv)) scheme.Reset();
2400 :
2401 1314 : nsAutoCString protocol(Segment(scheme));
2402 0 : nsAutoCString baseProtocol(Scheme());
2403 :
2404 : // We need to do backslash replacement for the following cases:
2405 : // 1. The input is an absolute path with a http/https/ftp scheme
2406 : // 2. The input is a relative path, and the base URL has a http/https/ftp scheme
2407 1314 : if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) ||
2408 0 : IsSpecialProtocol(protocol)) {
2409 :
2410 0 : nsAutoCString::iterator start;
2411 0 : nsAutoCString::iterator end;
2412 0 : buf.BeginWriting(start);
2413 0 : buf.EndWriting(end);
2414 0 : while (start != end) {
2415 0 : if (*start == '?' || *start == '#') {
2416 : break;
2417 : }
2418 0 : if (*start == '\\') {
2419 0 : *start = '/';
2420 : }
2421 0 : start++;
2422 : }
2423 : }
2424 :
2425 657 : if (scheme.mLen >= 0) {
2426 : // add some flags to coalesceFlag if it is an ftp-url
2427 : // need this later on when coalescing the resulting URL
2428 0 : if (SegmentIs(relpath, scheme, "ftp", true)) {
2429 0 : coalesceFlag = (netCoalesceFlags) (coalesceFlag
2430 : | NET_COALESCE_ALLOW_RELATIVE_ROOT
2431 : | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
2432 :
2433 : }
2434 : // this URL appears to be absolute
2435 : // but try to find out more
2436 0 : if (SegmentIs(mScheme, relpath, scheme, true)) {
2437 : // mScheme and Scheme are the same
2438 : // but this can still be relative
2439 0 : if (strncmp(relpath + scheme.mPos + scheme.mLen, "://", 3) == 0) {
2440 : // now this is really absolute
2441 : // because a :// follows the scheme
2442 0 : result = NS_strdup(relpath);
2443 : } else {
2444 : // This is a deprecated form of relative urls like
2445 : // http:file or http:/path/file
2446 : // we will support it for now ...
2447 0 : relative = true;
2448 0 : offset = scheme.mLen + 1;
2449 : }
2450 : } else {
2451 : // the schemes are not the same, we are also done
2452 : // because we have to assume this is absolute
2453 0 : result = NS_strdup(relpath);
2454 : }
2455 : } else {
2456 : // add some flags to coalesceFlag if it is an ftp-url
2457 : // need this later on when coalescing the resulting URL
2458 657 : if (SegmentIs(mScheme,"ftp")) {
2459 0 : coalesceFlag = (netCoalesceFlags) (coalesceFlag
2460 : | NET_COALESCE_ALLOW_RELATIVE_ROOT
2461 : | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
2462 : }
2463 657 : if (relpath[0] == '/' && relpath[1] == '/') {
2464 : // this URL //host/path is almost absolute
2465 0 : result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
2466 : } else {
2467 : // then it must be relative
2468 : relative = true;
2469 : }
2470 : }
2471 657 : if (relative) {
2472 0 : uint32_t len = 0;
2473 0 : const char *realrelpath = relpath + offset;
2474 0 : switch (*realrelpath) {
2475 : case '/':
2476 : // overwrite everything after the authority
2477 0 : len = mAuthority.mPos + mAuthority.mLen;
2478 0 : break;
2479 : case '?':
2480 : // overwrite the existing ?query and #ref
2481 0 : if (mQuery.mLen >= 0)
2482 0 : len = mQuery.mPos - 1;
2483 0 : else if (mRef.mLen >= 0)
2484 0 : len = mRef.mPos - 1;
2485 : else
2486 0 : len = mPath.mPos + mPath.mLen;
2487 : break;
2488 : case '#':
2489 : case '\0':
2490 : // overwrite the existing #ref
2491 2 : if (mRef.mLen < 0)
2492 0 : len = mPath.mPos + mPath.mLen;
2493 : else
2494 0 : len = mRef.mPos - 1;
2495 : break;
2496 : default:
2497 655 : if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
2498 0 : if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
2499 0 : nsCaseInsensitiveCStringComparator())) {
2500 : // if ftp URL ends with %2F then simply
2501 : // append relative part because %2F also
2502 : // marks the root directory with ftp-urls
2503 0 : len = mFilepath.mPos + mFilepath.mLen;
2504 : } else {
2505 : // overwrite everything after the directory
2506 0 : len = mDirectory.mPos + mDirectory.mLen;
2507 : }
2508 : } else {
2509 : // overwrite everything after the directory
2510 655 : len = mDirectory.mPos + mDirectory.mLen;
2511 : }
2512 : }
2513 657 : result = AppendToSubstring(0, len, realrelpath);
2514 : // locate result path
2515 657 : resultPath = result + mPath.mPos;
2516 : }
2517 657 : if (!result)
2518 : return NS_ERROR_OUT_OF_MEMORY;
2519 :
2520 657 : if (resultPath)
2521 0 : net_CoalesceDirs(coalesceFlag, resultPath);
2522 : else {
2523 : // locate result path
2524 0 : resultPath = PL_strstr(result, "://");
2525 0 : if (resultPath) {
2526 0 : resultPath = PL_strchr(resultPath + 3, '/');
2527 0 : if (resultPath)
2528 0 : net_CoalesceDirs(coalesceFlag,resultPath);
2529 : }
2530 : }
2531 657 : out.Adopt(result);
2532 0 : return NS_OK;
2533 : }
2534 :
2535 : // result may contain unescaped UTF-8 characters
2536 : NS_IMETHODIMP
2537 0 : nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
2538 : {
2539 0 : NS_ENSURE_ARG_POINTER(uri2);
2540 :
2541 : // if uri's are equal, then return uri as is
2542 0 : bool isEquals = false;
2543 0 : if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
2544 0 : return GetSpec(aResult);
2545 :
2546 0 : aResult.Truncate();
2547 :
2548 : // check pre-path; if they don't match, then return empty string
2549 : nsStandardURL *stdurl2;
2550 0 : nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
2551 0 : isEquals = NS_SUCCEEDED(rv)
2552 0 : && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
2553 0 : && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
2554 0 : && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
2555 0 : && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
2556 0 : && (Port() == stdurl2->Port());
2557 0 : if (!isEquals)
2558 : {
2559 0 : if (NS_SUCCEEDED(rv))
2560 0 : NS_RELEASE(stdurl2);
2561 : return NS_OK;
2562 : }
2563 :
2564 : // scan for first mismatched character
2565 : const char *thisIndex, *thatIndex, *startCharPos;
2566 0 : startCharPos = mSpec.get() + mDirectory.mPos;
2567 0 : thisIndex = startCharPos;
2568 0 : thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
2569 0 : while ((*thisIndex == *thatIndex) && *thisIndex)
2570 : {
2571 0 : thisIndex++;
2572 0 : thatIndex++;
2573 : }
2574 :
2575 : // backup to just after previous slash so we grab an appropriate path
2576 : // segment such as a directory (not partial segments)
2577 : // todo: also check for file matches which include '?' and '#'
2578 0 : while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/'))
2579 0 : thisIndex--;
2580 :
2581 : // grab spec from beginning to thisIndex
2582 0 : aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
2583 :
2584 0 : NS_RELEASE(stdurl2);
2585 0 : return rv;
2586 : }
2587 :
2588 : NS_IMETHODIMP
2589 0 : nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
2590 : {
2591 0 : NS_ENSURE_ARG_POINTER(uri2);
2592 :
2593 0 : aResult.Truncate();
2594 :
2595 : // if uri's are equal, then return empty string
2596 0 : bool isEquals = false;
2597 0 : if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
2598 : return NS_OK;
2599 :
2600 : nsStandardURL *stdurl2;
2601 0 : nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
2602 0 : isEquals = NS_SUCCEEDED(rv)
2603 0 : && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
2604 0 : && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
2605 0 : && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
2606 0 : && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
2607 0 : && (Port() == stdurl2->Port());
2608 0 : if (!isEquals)
2609 : {
2610 0 : if (NS_SUCCEEDED(rv))
2611 0 : NS_RELEASE(stdurl2);
2612 :
2613 0 : return uri2->GetSpec(aResult);
2614 : }
2615 :
2616 : // scan for first mismatched character
2617 : const char *thisIndex, *thatIndex, *startCharPos;
2618 0 : startCharPos = mSpec.get() + mDirectory.mPos;
2619 0 : thisIndex = startCharPos;
2620 0 : thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
2621 :
2622 : #ifdef XP_WIN
2623 : bool isFileScheme = SegmentIs(mScheme, "file");
2624 : if (isFileScheme)
2625 : {
2626 : // on windows, we need to match the first segment of the path
2627 : // if these don't match then we need to return an absolute path
2628 : // skip over any leading '/' in path
2629 : while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
2630 : {
2631 : thisIndex++;
2632 : thatIndex++;
2633 : }
2634 : // look for end of first segment
2635 : while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
2636 : {
2637 : thisIndex++;
2638 : thatIndex++;
2639 : }
2640 :
2641 : // if we didn't match through the first segment, return absolute path
2642 : if ((*thisIndex != '/') || (*thatIndex != '/'))
2643 : {
2644 : NS_RELEASE(stdurl2);
2645 : return uri2->GetSpec(aResult);
2646 : }
2647 : }
2648 : #endif
2649 :
2650 0 : while ((*thisIndex == *thatIndex) && *thisIndex)
2651 : {
2652 0 : thisIndex++;
2653 0 : thatIndex++;
2654 : }
2655 :
2656 : // backup to just after previous slash so we grab an appropriate path
2657 : // segment such as a directory (not partial segments)
2658 : // todo: also check for file matches with '#' and '?'
2659 0 : while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
2660 0 : thatIndex--;
2661 :
2662 0 : const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen;
2663 :
2664 : // need to account for slashes and add corresponding "../"
2665 0 : for (; thisIndex <= limit && *thisIndex; ++thisIndex)
2666 : {
2667 0 : if (*thisIndex == '/')
2668 0 : aResult.AppendLiteral("../");
2669 : }
2670 :
2671 : // grab spec from thisIndex to end
2672 0 : uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
2673 0 : aResult.Append(Substring(stdurl2->mSpec, startPos,
2674 0 : stdurl2->mSpec.Length() - startPos));
2675 :
2676 0 : NS_RELEASE(stdurl2);
2677 0 : return rv;
2678 : }
2679 :
2680 : //----------------------------------------------------------------------------
2681 : // nsStandardURL::nsIURL
2682 : //----------------------------------------------------------------------------
2683 :
2684 : // result may contain unescaped UTF-8 characters
2685 : NS_IMETHODIMP
2686 1407 : nsStandardURL::GetFilePath(nsACString &result)
2687 : {
2688 4221 : result = Filepath();
2689 0 : return NS_OK;
2690 : }
2691 :
2692 : // result may contain unescaped UTF-8 characters
2693 : NS_IMETHODIMP
2694 4 : nsStandardURL::GetQuery(nsACString &result)
2695 : {
2696 12 : result = Query();
2697 0 : return NS_OK;
2698 : }
2699 :
2700 : // result may contain unescaped UTF-8 characters
2701 : NS_IMETHODIMP
2702 559 : nsStandardURL::GetRef(nsACString &result)
2703 : {
2704 1677 : result = Ref();
2705 0 : return NS_OK;
2706 : }
2707 :
2708 : NS_IMETHODIMP
2709 6 : nsStandardURL::GetHasRef(bool *result)
2710 : {
2711 6 : *result = (mRef.mLen >= 0);
2712 0 : return NS_OK;
2713 : }
2714 :
2715 : // result may contain unescaped UTF-8 characters
2716 : NS_IMETHODIMP
2717 1 : nsStandardURL::GetDirectory(nsACString &result)
2718 : {
2719 3 : result = Directory();
2720 0 : return NS_OK;
2721 : }
2722 :
2723 : // result may contain unescaped UTF-8 characters
2724 : NS_IMETHODIMP
2725 0 : nsStandardURL::GetFileName(nsACString &result)
2726 : {
2727 0 : result = Filename();
2728 0 : return NS_OK;
2729 : }
2730 :
2731 : // result may contain unescaped UTF-8 characters
2732 : NS_IMETHODIMP
2733 0 : nsStandardURL::GetFileBaseName(nsACString &result)
2734 : {
2735 0 : result = Basename();
2736 0 : return NS_OK;
2737 : }
2738 :
2739 : // result may contain unescaped UTF-8 characters
2740 : NS_IMETHODIMP
2741 5 : nsStandardURL::GetFileExtension(nsACString &result)
2742 : {
2743 15 : result = Extension();
2744 0 : return NS_OK;
2745 : }
2746 :
2747 : nsresult
2748 0 : nsStandardURL::SetFilePath(const nsACString &input)
2749 : {
2750 0 : const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2751 0 : const char *filepath = flat.get();
2752 :
2753 0 : LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
2754 :
2755 : // if there isn't a filepath, then there can't be anything
2756 : // after the path either. this url is likely uninitialized.
2757 0 : if (mFilepath.mLen < 0)
2758 0 : return SetPathQueryRef(flat);
2759 :
2760 0 : if (filepath && *filepath) {
2761 0 : nsAutoCString spec;
2762 : uint32_t dirPos, basePos, extPos;
2763 : int32_t dirLen, baseLen, extLen;
2764 : nsresult rv;
2765 :
2766 0 : rv = mParser->ParseFilePath(filepath, flat.Length(),
2767 : &dirPos, &dirLen,
2768 : &basePos, &baseLen,
2769 0 : &extPos, &extLen);
2770 0 : if (NS_FAILED(rv)) return rv;
2771 :
2772 : // build up new candidate spec
2773 0 : spec.Assign(mSpec.get(), mPath.mPos);
2774 :
2775 : // ensure leading '/'
2776 0 : if (filepath[dirPos] != '/')
2777 0 : spec.Append('/');
2778 :
2779 0 : nsSegmentEncoder encoder;
2780 :
2781 : // append encoded filepath components
2782 0 : if (dirLen > 0)
2783 0 : encoder.EncodeSegment(Substring(filepath + dirPos,
2784 0 : filepath + dirPos + dirLen),
2785 0 : esc_Directory | esc_AlwaysCopy, spec);
2786 0 : if (baseLen > 0)
2787 0 : encoder.EncodeSegment(Substring(filepath + basePos,
2788 0 : filepath + basePos + baseLen),
2789 0 : esc_FileBaseName | esc_AlwaysCopy, spec);
2790 0 : if (extLen >= 0) {
2791 0 : spec.Append('.');
2792 0 : if (extLen > 0)
2793 0 : encoder.EncodeSegment(Substring(filepath + extPos,
2794 0 : filepath + extPos + extLen),
2795 : esc_FileExtension | esc_AlwaysCopy,
2796 0 : spec);
2797 : }
2798 :
2799 : // compute the ending position of the current filepath
2800 0 : if (mFilepath.mLen >= 0) {
2801 0 : uint32_t end = mFilepath.mPos + mFilepath.mLen;
2802 0 : if (mSpec.Length() > end)
2803 0 : spec.Append(mSpec.get() + end, mSpec.Length() - end);
2804 : }
2805 :
2806 0 : return SetSpecInternal(spec);
2807 : }
2808 0 : if (mPath.mLen > 1) {
2809 0 : mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
2810 : // left shift query, and ref
2811 0 : ShiftFromQuery(1 - mFilepath.mLen);
2812 : // these contain only a '/'
2813 0 : mPath.mLen = 1;
2814 0 : mDirectory.mLen = 1;
2815 0 : mFilepath.mLen = 1;
2816 : // these are no longer defined
2817 0 : mBasename.mLen = -1;
2818 0 : mExtension.mLen = -1;
2819 : }
2820 : return NS_OK;
2821 : }
2822 :
2823 : inline bool
2824 2440 : IsUTFEncoding(const Encoding* aEncoding)
2825 : {
2826 7194 : return aEncoding == UTF_8_ENCODING ||
2827 0 : aEncoding == UTF_16BE_ENCODING ||
2828 0 : aEncoding == UTF_16LE_ENCODING;
2829 : }
2830 :
2831 : nsresult
2832 1 : nsStandardURL::SetQuery(const nsACString &input)
2833 : {
2834 1 : return SetQueryWithEncoding(input, nullptr);
2835 : }
2836 :
2837 : nsresult
2838 1 : nsStandardURL::SetQueryWithEncoding(const nsACString &input,
2839 : const Encoding* encoding)
2840 : {
2841 2 : const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2842 0 : const char *query = flat.get();
2843 :
2844 1 : LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
2845 :
2846 1 : if (IsUTFEncoding(encoding)) {
2847 0 : encoding = nullptr;
2848 : }
2849 :
2850 1 : if (mPath.mLen < 0)
2851 0 : return SetPathQueryRef(flat);
2852 :
2853 2 : if (mSpec.Length() + input.Length() - Query().Length() > (uint32_t) net_GetURLMaxLength()) {
2854 : return NS_ERROR_MALFORMED_URI;
2855 : }
2856 :
2857 1 : InvalidateCache();
2858 :
2859 1 : if (!query || !*query) {
2860 : // remove existing query
2861 0 : if (mQuery.mLen >= 0) {
2862 : // remove query and leading '?'
2863 0 : mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
2864 0 : ShiftFromRef(-(mQuery.mLen + 1));
2865 0 : mPath.mLen -= (mQuery.mLen + 1);
2866 0 : mQuery.mPos = 0;
2867 0 : mQuery.mLen = -1;
2868 : }
2869 : return NS_OK;
2870 : }
2871 :
2872 1 : int32_t queryLen = flat.Length();
2873 0 : if (query[0] == '?') {
2874 0 : query++;
2875 0 : queryLen--;
2876 : }
2877 :
2878 1 : if (mQuery.mLen < 0) {
2879 0 : if (mRef.mLen < 0)
2880 0 : mQuery.mPos = mSpec.Length();
2881 : else
2882 0 : mQuery.mPos = mRef.mPos - 1;
2883 0 : mSpec.Insert('?', mQuery.mPos);
2884 0 : mQuery.mPos++;
2885 0 : mQuery.mLen = 0;
2886 : // the insertion pushes these out by 1
2887 1 : mPath.mLen++;
2888 0 : mRef.mPos++;
2889 : }
2890 :
2891 : // encode query if necessary
2892 1 : nsAutoCString buf;
2893 : bool encoded;
2894 1 : nsSegmentEncoder encoder(encoding);
2895 0 : encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
2896 0 : buf, encoded);
2897 0 : if (encoded) {
2898 0 : query = buf.get();
2899 0 : queryLen = buf.Length();
2900 : }
2901 :
2902 1 : int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
2903 :
2904 1 : if (shift) {
2905 0 : mQuery.mLen = queryLen;
2906 0 : mPath.mLen += shift;
2907 0 : ShiftFromRef(shift);
2908 : }
2909 1 : return NS_OK;
2910 : }
2911 :
2912 : nsresult
2913 546 : nsStandardURL::SetRef(const nsACString &input)
2914 : {
2915 1092 : const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2916 0 : const char *ref = flat.get();
2917 :
2918 546 : LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
2919 :
2920 546 : if (mPath.mLen < 0)
2921 0 : return SetPathQueryRef(flat);
2922 :
2923 1092 : if (mSpec.Length() + input.Length() - Ref().Length() > (uint32_t) net_GetURLMaxLength()) {
2924 : return NS_ERROR_MALFORMED_URI;
2925 : }
2926 :
2927 546 : InvalidateCache();
2928 :
2929 546 : if (!ref || !*ref) {
2930 : // remove existing ref
2931 462 : if (mRef.mLen >= 0) {
2932 : // remove ref and leading '#'
2933 423 : mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
2934 0 : mPath.mLen -= (mRef.mLen + 1);
2935 0 : mRef.mPos = 0;
2936 0 : mRef.mLen = -1;
2937 : }
2938 : return NS_OK;
2939 : }
2940 :
2941 84 : int32_t refLen = flat.Length();
2942 0 : if (ref[0] == '#') {
2943 0 : ref++;
2944 0 : refLen--;
2945 : }
2946 :
2947 84 : if (mRef.mLen < 0) {
2948 0 : mSpec.Append('#');
2949 0 : ++mPath.mLen; // Include the # in the path.
2950 0 : mRef.mPos = mSpec.Length();
2951 0 : mRef.mLen = 0;
2952 : }
2953 :
2954 : // If precent encoding is necessary, `ref` will point to `buf`'s content.
2955 : // `buf` needs to outlive any use of the `ref` pointer.
2956 84 : nsAutoCString buf;
2957 : // encode ref if necessary
2958 : bool encoded;
2959 84 : nsSegmentEncoder encoder;
2960 0 : encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
2961 0 : buf, encoded);
2962 0 : if (encoded) {
2963 0 : ref = buf.get();
2964 0 : refLen = buf.Length();
2965 : }
2966 :
2967 84 : int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
2968 0 : mPath.mLen += shift;
2969 0 : mRef.mLen = refLen;
2970 0 : return NS_OK;
2971 : }
2972 :
2973 : nsresult
2974 0 : nsStandardURL::SetFileNameInternal(const nsACString &input)
2975 : {
2976 0 : const nsPromiseFlatCString &flat = PromiseFlatCString(input);
2977 0 : const char *filename = flat.get();
2978 :
2979 0 : LOG(("nsStandardURL::SetFileNameInternal [filename=%s]\n", filename));
2980 :
2981 0 : if (mPath.mLen < 0)
2982 0 : return SetPathQueryRef(flat);
2983 :
2984 0 : if (mSpec.Length() + input.Length() - Filename().Length() > (uint32_t) net_GetURLMaxLength()) {
2985 : return NS_ERROR_MALFORMED_URI;
2986 : }
2987 :
2988 0 : int32_t shift = 0;
2989 :
2990 0 : if (!(filename && *filename)) {
2991 : // remove the filename
2992 0 : if (mBasename.mLen > 0) {
2993 0 : if (mExtension.mLen >= 0)
2994 0 : mBasename.mLen += (mExtension.mLen + 1);
2995 0 : mSpec.Cut(mBasename.mPos, mBasename.mLen);
2996 0 : shift = -mBasename.mLen;
2997 0 : mBasename.mLen = 0;
2998 0 : mExtension.mLen = -1;
2999 : }
3000 : }
3001 : else {
3002 : nsresult rv;
3003 0 : URLSegment basename, extension;
3004 :
3005 : // let the parser locate the basename and extension
3006 0 : rv = mParser->ParseFileName(filename, flat.Length(),
3007 : &basename.mPos, &basename.mLen,
3008 0 : &extension.mPos, &extension.mLen);
3009 0 : if (NS_FAILED(rv)) return rv;
3010 :
3011 0 : if (basename.mLen < 0) {
3012 : // remove existing filename
3013 0 : if (mBasename.mLen >= 0) {
3014 0 : uint32_t len = mBasename.mLen;
3015 0 : if (mExtension.mLen >= 0)
3016 0 : len += (mExtension.mLen + 1);
3017 0 : mSpec.Cut(mBasename.mPos, len);
3018 0 : shift = -int32_t(len);
3019 0 : mBasename.mLen = 0;
3020 0 : mExtension.mLen = -1;
3021 : }
3022 : }
3023 : else {
3024 0 : nsAutoCString newFilename;
3025 : bool ignoredOut;
3026 0 : nsSegmentEncoder encoder;
3027 0 : basename.mLen = encoder.EncodeSegmentCount(filename, basename,
3028 : esc_FileBaseName |
3029 : esc_AlwaysCopy,
3030 : newFilename,
3031 : ignoredOut);
3032 0 : if (extension.mLen >= 0) {
3033 0 : newFilename.Append('.');
3034 0 : extension.mLen = encoder.EncodeSegmentCount(filename, extension,
3035 : esc_FileExtension |
3036 : esc_AlwaysCopy,
3037 : newFilename,
3038 : ignoredOut);
3039 : }
3040 :
3041 0 : if (mBasename.mLen < 0) {
3042 : // insert new filename
3043 0 : mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
3044 0 : mSpec.Insert(newFilename, mBasename.mPos);
3045 0 : shift = newFilename.Length();
3046 : }
3047 : else {
3048 : // replace existing filename
3049 0 : uint32_t oldLen = uint32_t(mBasename.mLen);
3050 0 : if (mExtension.mLen >= 0)
3051 0 : oldLen += (mExtension.mLen + 1);
3052 0 : mSpec.Replace(mBasename.mPos, oldLen, newFilename);
3053 0 : shift = newFilename.Length() - oldLen;
3054 : }
3055 :
3056 0 : mBasename.mLen = basename.mLen;
3057 0 : mExtension.mLen = extension.mLen;
3058 0 : if (mExtension.mLen >= 0)
3059 0 : mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
3060 : }
3061 : }
3062 0 : if (shift) {
3063 0 : ShiftFromQuery(shift);
3064 0 : mFilepath.mLen += shift;
3065 0 : mPath.mLen += shift;
3066 : }
3067 : return NS_OK;
3068 : }
3069 :
3070 : nsresult
3071 0 : nsStandardURL::SetFileBaseNameInternal(const nsACString &input)
3072 : {
3073 0 : nsAutoCString extension;
3074 0 : nsresult rv = GetFileExtension(extension);
3075 0 : NS_ENSURE_SUCCESS(rv, rv);
3076 :
3077 0 : nsAutoCString newFileName(input);
3078 :
3079 0 : if (!extension.IsEmpty()) {
3080 0 : newFileName.Append('.');
3081 0 : newFileName.Append(extension);
3082 : }
3083 :
3084 0 : return SetFileNameInternal(newFileName);
3085 : }
3086 :
3087 : nsresult
3088 0 : nsStandardURL::SetFileExtensionInternal(const nsACString &input)
3089 : {
3090 0 : nsAutoCString newFileName;
3091 0 : nsresult rv = GetFileBaseName(newFileName);
3092 0 : NS_ENSURE_SUCCESS(rv, rv);
3093 :
3094 0 : if (!input.IsEmpty()) {
3095 0 : newFileName.Append('.');
3096 0 : newFileName.Append(input);
3097 : }
3098 :
3099 0 : return SetFileNameInternal(newFileName);
3100 : }
3101 :
3102 : //----------------------------------------------------------------------------
3103 : // nsStandardURL::nsIFileURL
3104 : //----------------------------------------------------------------------------
3105 :
3106 : nsresult
3107 675 : nsStandardURL::EnsureFile()
3108 : {
3109 675 : MOZ_ASSERT(mSupportsFileURL,
3110 : "EnsureFile() called on a URL that doesn't support files!");
3111 :
3112 1350 : if (mFile) {
3113 : // Nothing to do
3114 : return NS_OK;
3115 : }
3116 :
3117 : // Parse the spec if we don't have a cached result
3118 494 : if (mSpec.IsEmpty()) {
3119 0 : NS_WARNING("url not initialized");
3120 0 : return NS_ERROR_NOT_INITIALIZED;
3121 : }
3122 :
3123 494 : if (!SegmentIs(mScheme, "file")) {
3124 0 : NS_WARNING("not a file URL");
3125 0 : return NS_ERROR_FAILURE;
3126 : }
3127 :
3128 988 : return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
3129 : }
3130 :
3131 : NS_IMETHODIMP
3132 855 : nsStandardURL::GetFile(nsIFile **result)
3133 : {
3134 855 : MOZ_ASSERT(mSupportsFileURL,
3135 : "GetFile() called on a URL that doesn't support files!");
3136 :
3137 855 : nsresult rv = EnsureFile();
3138 0 : if (NS_FAILED(rv))
3139 : return rv;
3140 :
3141 675 : if (LOG_ENABLED()) {
3142 0 : LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
3143 : this, mSpec.get(), mFile->HumanReadablePath().get()));
3144 : }
3145 :
3146 : // clone the file, so the caller can modify it.
3147 : // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
3148 : // nsIFile returned from this method; but it seems that some folks do
3149 : // (see bug 161921). until we can be sure that all the consumers are
3150 : // behaving themselves, we'll stay on the safe side and clone the file.
3151 : // see bug 212724 about fixing the consumers.
3152 675 : return mFile->Clone(result);
3153 : }
3154 :
3155 : nsresult
3156 38 : nsStandardURL::SetFile(nsIFile *file)
3157 : {
3158 38 : NS_ENSURE_ARG_POINTER(file);
3159 :
3160 : nsresult rv;
3161 38 : nsAutoCString url;
3162 :
3163 38 : rv = net_GetURLSpecFromFile(file, url);
3164 0 : if (NS_FAILED(rv)) return rv;
3165 :
3166 38 : uint32_t oldURLType = mURLType;
3167 0 : uint32_t oldDefaultPort = mDefaultPort;
3168 0 : rv = Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, url, nullptr, nullptr);
3169 :
3170 38 : if (NS_FAILED(rv)) {
3171 : // Restore the old url type and default port if the call to Init fails.
3172 0 : mURLType = oldURLType;
3173 0 : mDefaultPort = oldDefaultPort;
3174 0 : return rv;
3175 : }
3176 :
3177 : // must clone |file| since its value is not guaranteed to remain constant
3178 38 : InvalidateCache();
3179 0 : if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
3180 0 : NS_WARNING("nsIFile::Clone failed");
3181 : // failure to clone is not fatal (GetFile will generate mFile)
3182 0 : mFile = nullptr;
3183 : }
3184 :
3185 : return NS_OK;
3186 : }
3187 :
3188 : //----------------------------------------------------------------------------
3189 : // nsStandardURL::nsIStandardURL
3190 : //----------------------------------------------------------------------------
3191 :
3192 : nsresult
3193 2439 : nsStandardURL::Init(uint32_t urlType,
3194 : int32_t defaultPort,
3195 : const nsACString &spec,
3196 : const char *charset,
3197 : nsIURI *baseURI)
3198 : {
3199 2439 : if (spec.Length() > (uint32_t) net_GetURLMaxLength() ||
3200 : defaultPort > std::numeric_limits<uint16_t>::max()) {
3201 : return NS_ERROR_MALFORMED_URI;
3202 : }
3203 :
3204 2439 : InvalidateCache();
3205 :
3206 2439 : switch (urlType) {
3207 : case URLTYPE_STANDARD:
3208 712 : mParser = net_GetStdURLParser();
3209 0 : break;
3210 : case URLTYPE_AUTHORITY:
3211 93 : mParser = net_GetAuthURLParser();
3212 0 : break;
3213 : case URLTYPE_NO_AUTHORITY:
3214 1634 : mParser = net_GetNoAuthURLParser();
3215 0 : break;
3216 : default:
3217 0 : NS_NOTREACHED("bad urlType");
3218 0 : return NS_ERROR_INVALID_ARG;
3219 : }
3220 2439 : mDefaultPort = defaultPort;
3221 0 : mURLType = urlType;
3222 :
3223 : auto encoding =
3224 2439 : charset ? Encoding::ForLabelNoReplacement(MakeStringSpan(charset))
3225 0 : : nullptr;
3226 : // URI can't be encoded in UTF-16BE or UTF-16LE. Truncate encoding
3227 : // if it is one of utf encodings (since a null encoding implies
3228 : // UTF-8, this is safe even if encoding is UTF-8).
3229 2439 : if (IsUTFEncoding(encoding)) {
3230 0 : encoding = nullptr;
3231 : }
3232 :
3233 2439 : if (baseURI && net_IsAbsoluteURL(spec)) {
3234 0 : baseURI = nullptr;
3235 : }
3236 :
3237 2439 : if (!baseURI)
3238 0 : return SetSpecWithEncoding(spec, encoding);
3239 :
3240 456 : nsAutoCString buf;
3241 0 : nsresult rv = baseURI->Resolve(spec, buf);
3242 0 : if (NS_FAILED(rv)) return rv;
3243 :
3244 456 : return SetSpecWithEncoding(buf, encoding);
3245 : }
3246 :
3247 : nsresult
3248 0 : nsStandardURL::SetDefaultPort(int32_t aNewDefaultPort)
3249 : {
3250 0 : InvalidateCache();
3251 :
3252 : // should never be more than 16 bit
3253 0 : if (aNewDefaultPort >= std::numeric_limits<uint16_t>::max()) {
3254 : return NS_ERROR_MALFORMED_URI;
3255 : }
3256 :
3257 : // If we're already using the new default-port as a custom port, then clear
3258 : // it off of our mSpec & set mPort to -1, to indicate that we'll be using
3259 : // the default from now on (which happens to match what we already had).
3260 0 : if (mPort == aNewDefaultPort) {
3261 0 : ReplacePortInSpec(-1);
3262 0 : mPort = -1;
3263 : }
3264 0 : mDefaultPort = aNewDefaultPort;
3265 :
3266 0 : return NS_OK;
3267 : }
3268 :
3269 : //----------------------------------------------------------------------------
3270 : // nsStandardURL::nsISerializable
3271 : //----------------------------------------------------------------------------
3272 :
3273 : NS_IMETHODIMP
3274 0 : nsStandardURL::Read(nsIObjectInputStream *stream)
3275 : {
3276 0 : NS_NOTREACHED("Use nsIURIMutator.read() instead");
3277 0 : return NS_ERROR_NOT_IMPLEMENTED;
3278 : }
3279 :
3280 : nsresult
3281 0 : nsStandardURL::ReadPrivate(nsIObjectInputStream *stream)
3282 : {
3283 0 : MOZ_ASSERT(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
3284 :
3285 : nsresult rv;
3286 :
3287 : uint32_t urlType;
3288 0 : rv = stream->Read32(&urlType);
3289 0 : if (NS_FAILED(rv)) return rv;
3290 0 : mURLType = urlType;
3291 0 : switch (mURLType) {
3292 : case URLTYPE_STANDARD:
3293 0 : mParser = net_GetStdURLParser();
3294 0 : break;
3295 : case URLTYPE_AUTHORITY:
3296 0 : mParser = net_GetAuthURLParser();
3297 0 : break;
3298 : case URLTYPE_NO_AUTHORITY:
3299 0 : mParser = net_GetNoAuthURLParser();
3300 0 : break;
3301 : default:
3302 0 : NS_NOTREACHED("bad urlType");
3303 0 : return NS_ERROR_FAILURE;
3304 : }
3305 :
3306 0 : rv = stream->Read32((uint32_t *) &mPort);
3307 0 : if (NS_FAILED(rv)) return rv;
3308 :
3309 0 : rv = stream->Read32((uint32_t *) &mDefaultPort);
3310 0 : if (NS_FAILED(rv)) return rv;
3311 :
3312 0 : rv = NS_ReadOptionalCString(stream, mSpec);
3313 0 : if (NS_FAILED(rv)) return rv;
3314 :
3315 0 : rv = ReadSegment(stream, mScheme);
3316 0 : if (NS_FAILED(rv)) return rv;
3317 :
3318 0 : rv = ReadSegment(stream, mAuthority);
3319 0 : if (NS_FAILED(rv)) return rv;
3320 :
3321 0 : rv = ReadSegment(stream, mUsername);
3322 0 : if (NS_FAILED(rv)) return rv;
3323 :
3324 0 : rv = ReadSegment(stream, mPassword);
3325 0 : if (NS_FAILED(rv)) return rv;
3326 :
3327 0 : rv = ReadSegment(stream, mHost);
3328 0 : if (NS_FAILED(rv)) return rv;
3329 :
3330 0 : rv = ReadSegment(stream, mPath);
3331 0 : if (NS_FAILED(rv)) return rv;
3332 :
3333 0 : rv = ReadSegment(stream, mFilepath);
3334 0 : if (NS_FAILED(rv)) return rv;
3335 :
3336 0 : rv = ReadSegment(stream, mDirectory);
3337 0 : if (NS_FAILED(rv)) return rv;
3338 :
3339 0 : rv = ReadSegment(stream, mBasename);
3340 0 : if (NS_FAILED(rv)) return rv;
3341 :
3342 0 : rv = ReadSegment(stream, mExtension);
3343 0 : if (NS_FAILED(rv)) return rv;
3344 :
3345 : // handle forward compatibility from older serializations that included mParam
3346 0 : URLSegment old_param;
3347 0 : rv = ReadSegment(stream, old_param);
3348 0 : if (NS_FAILED(rv)) return rv;
3349 :
3350 0 : rv = ReadSegment(stream, mQuery);
3351 0 : if (NS_FAILED(rv)) return rv;
3352 :
3353 0 : rv = ReadSegment(stream, mRef);
3354 0 : if (NS_FAILED(rv)) return rv;
3355 :
3356 0 : nsAutoCString oldOriginCharset;
3357 0 : rv = NS_ReadOptionalCString(stream, oldOriginCharset);
3358 0 : if (NS_FAILED(rv)) return rv;
3359 :
3360 : bool isMutable;
3361 0 : rv = stream->ReadBoolean(&isMutable);
3362 0 : if (NS_FAILED(rv)) return rv;
3363 : Unused << isMutable;
3364 :
3365 : bool supportsFileURL;
3366 0 : rv = stream->ReadBoolean(&supportsFileURL);
3367 0 : if (NS_FAILED(rv)) return rv;
3368 0 : mSupportsFileURL = supportsFileURL;
3369 :
3370 : // wait until object is set up, then modify path to include the param
3371 0 : if (old_param.mLen >= 0) { // note that mLen=0 is ";"
3372 : // If this wasn't empty, it marks characters between the end of the
3373 : // file and start of the query - mPath should include the param,
3374 : // query and ref already. Bump the mFilePath and
3375 : // directory/basename/extension components to include this.
3376 0 : mFilepath.Merge(mSpec, ';', old_param);
3377 0 : mDirectory.Merge(mSpec, ';', old_param);
3378 0 : mBasename.Merge(mSpec, ';', old_param);
3379 0 : mExtension.Merge(mSpec, ';', old_param);
3380 : }
3381 :
3382 0 : rv = CheckIfHostIsAscii();
3383 0 : if (NS_FAILED(rv)) {
3384 : return rv;
3385 : }
3386 :
3387 0 : return NS_OK;
3388 : }
3389 :
3390 : NS_IMETHODIMP
3391 3 : nsStandardURL::Write(nsIObjectOutputStream *stream)
3392 : {
3393 3 : MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
3394 : "The spec should never be this long, we missed a check.");
3395 : nsresult rv;
3396 :
3397 3 : rv = stream->Write32(mURLType);
3398 0 : if (NS_FAILED(rv)) return rv;
3399 :
3400 3 : rv = stream->Write32(uint32_t(mPort));
3401 0 : if (NS_FAILED(rv)) return rv;
3402 :
3403 3 : rv = stream->Write32(uint32_t(mDefaultPort));
3404 0 : if (NS_FAILED(rv)) return rv;
3405 :
3406 3 : rv = NS_WriteOptionalStringZ(stream, mSpec.get());
3407 0 : if (NS_FAILED(rv)) return rv;
3408 :
3409 3 : rv = WriteSegment(stream, mScheme);
3410 0 : if (NS_FAILED(rv)) return rv;
3411 :
3412 3 : rv = WriteSegment(stream, mAuthority);
3413 0 : if (NS_FAILED(rv)) return rv;
3414 :
3415 3 : rv = WriteSegment(stream, mUsername);
3416 0 : if (NS_FAILED(rv)) return rv;
3417 :
3418 3 : rv = WriteSegment(stream, mPassword);
3419 0 : if (NS_FAILED(rv)) return rv;
3420 :
3421 3 : rv = WriteSegment(stream, mHost);
3422 0 : if (NS_FAILED(rv)) return rv;
3423 :
3424 3 : rv = WriteSegment(stream, mPath);
3425 0 : if (NS_FAILED(rv)) return rv;
3426 :
3427 3 : rv = WriteSegment(stream, mFilepath);
3428 0 : if (NS_FAILED(rv)) return rv;
3429 :
3430 3 : rv = WriteSegment(stream, mDirectory);
3431 0 : if (NS_FAILED(rv)) return rv;
3432 :
3433 3 : rv = WriteSegment(stream, mBasename);
3434 0 : if (NS_FAILED(rv)) return rv;
3435 :
3436 3 : rv = WriteSegment(stream, mExtension);
3437 0 : if (NS_FAILED(rv)) return rv;
3438 :
3439 : // for backwards compatibility since we removed mParam. Note that this will mean that
3440 : // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they
3441 : // after the removal of special handling). It only matters if you downgrade a browser to before
3442 : // the patch.
3443 3 : URLSegment empty;
3444 0 : rv = WriteSegment(stream, empty);
3445 0 : if (NS_FAILED(rv)) return rv;
3446 :
3447 3 : rv = WriteSegment(stream, mQuery);
3448 0 : if (NS_FAILED(rv)) return rv;
3449 :
3450 3 : rv = WriteSegment(stream, mRef);
3451 0 : if (NS_FAILED(rv)) return rv;
3452 :
3453 : // former origin charset
3454 3 : rv = NS_WriteOptionalStringZ(stream, EmptyCString().get());
3455 0 : if (NS_FAILED(rv)) return rv;
3456 :
3457 : // former mMutable
3458 3 : rv = stream->WriteBoolean(false);
3459 0 : if (NS_FAILED(rv)) return rv;
3460 :
3461 3 : rv = stream->WriteBoolean(mSupportsFileURL);
3462 0 : if (NS_FAILED(rv)) return rv;
3463 :
3464 : // mDisplayHost is just a cache that can be recovered as needed.
3465 :
3466 3 : return NS_OK;
3467 : }
3468 :
3469 : //---------------------------------------------------------------------------
3470 : // nsStandardURL::nsIIPCSerializableURI
3471 : //---------------------------------------------------------------------------
3472 :
3473 : inline
3474 : ipc::StandardURLSegment
3475 : ToIPCSegment(const nsStandardURL::URLSegment& aSegment)
3476 : {
3477 24 : return ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen);
3478 : }
3479 :
3480 : inline
3481 : MOZ_MUST_USE bool
3482 0 : FromIPCSegment(const nsACString& aSpec, const ipc::StandardURLSegment& aSegment, nsStandardURL::URLSegment& aTarget)
3483 : {
3484 : // This seems to be just an empty segment.
3485 0 : if (aSegment.length() == -1) {
3486 0 : aTarget = nsStandardURL::URLSegment();
3487 0 : return true;
3488 : }
3489 :
3490 : // A value of -1 means an empty segment, but < -1 is undefined.
3491 0 : if (NS_WARN_IF(aSegment.length() < -1)) {
3492 : return false;
3493 : }
3494 :
3495 0 : CheckedInt<uint32_t> segmentLen = aSegment.position();
3496 0 : segmentLen += aSegment.length();
3497 : // Make sure the segment does not extend beyond the spec.
3498 0 : if (NS_WARN_IF(!segmentLen.isValid() || segmentLen.value() > aSpec.Length())) {
3499 : return false;
3500 : }
3501 :
3502 0 : aTarget.mPos = aSegment.position();
3503 0 : aTarget.mLen = aSegment.length();
3504 :
3505 0 : return true;
3506 : }
3507 :
3508 : void
3509 1 : nsStandardURL::Serialize(URIParams& aParams)
3510 : {
3511 1 : MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
3512 : "The spec should never be this long, we missed a check.");
3513 2 : StandardURLParams params;
3514 :
3515 0 : params.urlType() = mURLType;
3516 0 : params.port() = mPort;
3517 0 : params.defaultPort() = mDefaultPort;
3518 0 : params.spec() = mSpec;
3519 0 : params.scheme() = ToIPCSegment(mScheme);
3520 0 : params.authority() = ToIPCSegment(mAuthority);
3521 0 : params.username() = ToIPCSegment(mUsername);
3522 0 : params.password() = ToIPCSegment(mPassword);
3523 0 : params.host() = ToIPCSegment(mHost);
3524 0 : params.path() = ToIPCSegment(mPath);
3525 0 : params.filePath() = ToIPCSegment(mFilepath);
3526 0 : params.directory() = ToIPCSegment(mDirectory);
3527 0 : params.baseName() = ToIPCSegment(mBasename);
3528 0 : params.extension() = ToIPCSegment(mExtension);
3529 0 : params.query() = ToIPCSegment(mQuery);
3530 0 : params.ref() = ToIPCSegment(mRef);
3531 1 : params.supportsFileURL() = !!mSupportsFileURL;
3532 : // mDisplayHost is just a cache that can be recovered as needed.
3533 :
3534 0 : aParams = params;
3535 1 : }
3536 :
3537 : bool
3538 0 : nsStandardURL::Deserialize(const URIParams& aParams)
3539 : {
3540 0 : MOZ_ASSERT(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
3541 0 : MOZ_ASSERT(!mFile, "Shouldn't have cached file");
3542 :
3543 0 : if (aParams.type() != URIParams::TStandardURLParams) {
3544 0 : NS_ERROR("Received unknown parameters from the other process!");
3545 0 : return false;
3546 : }
3547 :
3548 0 : const StandardURLParams& params = aParams.get_StandardURLParams();
3549 :
3550 0 : mURLType = params.urlType();
3551 0 : switch (mURLType) {
3552 : case URLTYPE_STANDARD:
3553 0 : mParser = net_GetStdURLParser();
3554 0 : break;
3555 : case URLTYPE_AUTHORITY:
3556 0 : mParser = net_GetAuthURLParser();
3557 0 : break;
3558 : case URLTYPE_NO_AUTHORITY:
3559 0 : mParser = net_GetNoAuthURLParser();
3560 0 : break;
3561 : default:
3562 0 : NS_NOTREACHED("bad urlType");
3563 0 : return false;
3564 : }
3565 :
3566 0 : mPort = params.port();
3567 0 : mDefaultPort = params.defaultPort();
3568 0 : mSpec = params.spec();
3569 0 : NS_ENSURE_TRUE(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(), false);
3570 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.scheme(), mScheme), false);
3571 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.authority(), mAuthority), false);
3572 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.username(), mUsername), false);
3573 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.password(), mPassword), false);
3574 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.host(), mHost), false);
3575 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.path(), mPath), false);
3576 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.filePath(), mFilepath), false);
3577 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.directory(), mDirectory), false);
3578 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.baseName(), mBasename), false);
3579 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.extension(), mExtension), false);
3580 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.query(), mQuery), false);
3581 0 : NS_ENSURE_TRUE(FromIPCSegment(mSpec, params.ref(), mRef), false);
3582 :
3583 0 : mSupportsFileURL = params.supportsFileURL();
3584 :
3585 0 : nsresult rv = CheckIfHostIsAscii();
3586 0 : if (NS_FAILED(rv)) {
3587 : return false;
3588 : }
3589 :
3590 : // Some sanity checks
3591 0 : NS_ENSURE_TRUE(mScheme.mPos == 0, false);
3592 0 : NS_ENSURE_TRUE(mScheme.mLen > 0, false);
3593 : // Make sure scheme is followed by :// (3 characters)
3594 0 : NS_ENSURE_TRUE(mScheme.mLen < INT32_MAX - 3, false); // avoid overflow
3595 0 : NS_ENSURE_TRUE(mSpec.Length() >= (uint32_t) mScheme.mLen + 3, false);
3596 0 : NS_ENSURE_TRUE(nsDependentCSubstring(mSpec, mScheme.mLen, 3).EqualsLiteral("://"), false);
3597 0 : NS_ENSURE_TRUE(mPath.mLen != -1 && mSpec.CharAt(mPath.mPos) == '/', false);
3598 0 : NS_ENSURE_TRUE(mPath.mPos == mFilepath.mPos, false);
3599 0 : NS_ENSURE_TRUE(mQuery.mLen == -1 || mSpec.CharAt(mQuery.mPos - 1) == '?', false);
3600 0 : NS_ENSURE_TRUE(mRef.mLen == -1 || mSpec.CharAt(mRef.mPos - 1) == '#', false);
3601 :
3602 : return true;
3603 : }
3604 :
3605 : //----------------------------------------------------------------------------
3606 : // nsStandardURL::nsIClassInfo
3607 : //----------------------------------------------------------------------------
3608 :
3609 : NS_IMETHODIMP
3610 136 : nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array)
3611 : {
3612 0 : *count = 0;
3613 0 : *array = nullptr;
3614 136 : return NS_OK;
3615 : }
3616 :
3617 : NS_IMETHODIMP
3618 141 : nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval)
3619 : {
3620 0 : *_retval = nullptr;
3621 141 : return NS_OK;
3622 : }
3623 :
3624 : NS_IMETHODIMP
3625 0 : nsStandardURL::GetContractID(nsACString& aContractID)
3626 : {
3627 0 : aContractID.SetIsVoid(true);
3628 0 : return NS_OK;
3629 : }
3630 :
3631 : NS_IMETHODIMP
3632 0 : nsStandardURL::GetClassDescription(nsACString& aClassDescription)
3633 : {
3634 0 : aClassDescription.SetIsVoid(true);
3635 0 : return NS_OK;
3636 : }
3637 :
3638 : NS_IMETHODIMP
3639 0 : nsStandardURL::GetClassID(nsCID * *aClassID)
3640 : {
3641 0 : *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID));
3642 0 : if (!*aClassID)
3643 : return NS_ERROR_OUT_OF_MEMORY;
3644 0 : return GetClassIDNoAlloc(*aClassID);
3645 : }
3646 :
3647 : NS_IMETHODIMP
3648 0 : nsStandardURL::GetFlags(uint32_t *aFlags)
3649 : {
3650 0 : *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
3651 0 : return NS_OK;
3652 : }
3653 :
3654 : NS_IMETHODIMP
3655 3 : nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
3656 : {
3657 0 : *aClassIDNoAlloc = kStandardURLCID;
3658 3 : return NS_OK;
3659 : }
3660 :
3661 : //----------------------------------------------------------------------------
3662 : // nsStandardURL::nsISizeOf
3663 : //----------------------------------------------------------------------------
3664 :
3665 : size_t
3666 0 : nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
3667 : {
3668 0 : return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
3669 0 : mDisplayHost.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
3670 :
3671 : // Measurement of the following members may be added later if DMD finds it is
3672 : // worthwhile:
3673 : // - mParser
3674 : // - mFile
3675 : }
3676 :
3677 : size_t
3678 0 : nsStandardURL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
3679 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
3680 : }
3681 :
3682 : } // namespace net
3683 : } // namespace mozilla
3684 :
3685 : // For unit tests. Including nsStandardURL.h seems to cause problems
3686 : nsresult
3687 0 : Test_NormalizeIPv4(const nsACString& host, nsCString& result)
3688 : {
3689 : return mozilla::net::nsStandardURL::NormalizeIPv4(host, result);
3690 : }
|