1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/docshell/base/nsDefaultURIFixup.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,862 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsNetUtil.h" 1.11 +#include "nsCRT.h" 1.12 + 1.13 +#include "nsIFile.h" 1.14 +#include <algorithm> 1.15 + 1.16 +#ifdef MOZ_TOOLKIT_SEARCH 1.17 +#include "nsIBrowserSearchService.h" 1.18 +#endif 1.19 + 1.20 +#include "nsIURIFixup.h" 1.21 +#include "nsDefaultURIFixup.h" 1.22 +#include "mozilla/Preferences.h" 1.23 +#include "mozilla/dom/ContentChild.h" 1.24 +#include "mozilla/ipc/InputStreamUtils.h" 1.25 +#include "mozilla/ipc/URIUtils.h" 1.26 +#include "nsIObserverService.h" 1.27 +#include "nsXULAppAPI.h" 1.28 + 1.29 +using namespace mozilla; 1.30 + 1.31 +/* Implementation file */ 1.32 +NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup) 1.33 + 1.34 +static bool sInitializedPrefCaches = false; 1.35 +static bool sFixTypos = true; 1.36 + 1.37 +nsDefaultURIFixup::nsDefaultURIFixup() 1.38 +{ 1.39 + /* member initializers and constructor code */ 1.40 +} 1.41 + 1.42 + 1.43 +nsDefaultURIFixup::~nsDefaultURIFixup() 1.44 +{ 1.45 + /* destructor code */ 1.46 +} 1.47 + 1.48 +/* nsIURI createExposableURI (in nsIURI aURI); */ 1.49 +NS_IMETHODIMP 1.50 +nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn) 1.51 +{ 1.52 + NS_ENSURE_ARG_POINTER(aURI); 1.53 + NS_ENSURE_ARG_POINTER(aReturn); 1.54 + 1.55 + bool isWyciwyg = false; 1.56 + aURI->SchemeIs("wyciwyg", &isWyciwyg); 1.57 + 1.58 + nsAutoCString userPass; 1.59 + aURI->GetUserPass(userPass); 1.60 + 1.61 + // most of the time we can just AddRef and return 1.62 + if (!isWyciwyg && userPass.IsEmpty()) 1.63 + { 1.64 + *aReturn = aURI; 1.65 + NS_ADDREF(*aReturn); 1.66 + return NS_OK; 1.67 + } 1.68 + 1.69 + // Rats, we have to massage the URI 1.70 + nsCOMPtr<nsIURI> uri; 1.71 + if (isWyciwyg) 1.72 + { 1.73 + nsAutoCString path; 1.74 + nsresult rv = aURI->GetPath(path); 1.75 + NS_ENSURE_SUCCESS(rv, rv); 1.76 + 1.77 + uint32_t pathLength = path.Length(); 1.78 + if (pathLength <= 2) 1.79 + { 1.80 + return NS_ERROR_FAILURE; 1.81 + } 1.82 + 1.83 + // Path is of the form "//123/http://foo/bar", with a variable number of digits. 1.84 + // To figure out where the "real" URL starts, search path for a '/', starting at 1.85 + // the third character. 1.86 + int32_t slashIndex = path.FindChar('/', 2); 1.87 + if (slashIndex == kNotFound) 1.88 + { 1.89 + return NS_ERROR_FAILURE; 1.90 + } 1.91 + 1.92 + // Get the charset of the original URI so we can pass it to our fixed up URI. 1.93 + nsAutoCString charset; 1.94 + aURI->GetOriginCharset(charset); 1.95 + 1.96 + rv = NS_NewURI(getter_AddRefs(uri), 1.97 + Substring(path, slashIndex + 1, pathLength - slashIndex - 1), 1.98 + charset.get()); 1.99 + NS_ENSURE_SUCCESS(rv, rv); 1.100 + } 1.101 + else 1.102 + { 1.103 + // clone the URI so zapping user:pass doesn't change the original 1.104 + nsresult rv = aURI->Clone(getter_AddRefs(uri)); 1.105 + NS_ENSURE_SUCCESS(rv, rv); 1.106 + } 1.107 + 1.108 + // hide user:pass unless overridden by pref 1.109 + if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) 1.110 + { 1.111 + uri->SetUserPass(EmptyCString()); 1.112 + } 1.113 + 1.114 + // return the fixed-up URI 1.115 + *aReturn = uri; 1.116 + NS_ADDREF(*aReturn); 1.117 + return NS_OK; 1.118 +} 1.119 + 1.120 +/* nsIURI createFixupURI (in nsAUTF8String aURIText, in unsigned long aFixupFlags); */ 1.121 +NS_IMETHODIMP 1.122 +nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags, 1.123 + nsIInputStream **aPostData, nsIURI **aURI) 1.124 +{ 1.125 + NS_ENSURE_ARG(!aStringURI.IsEmpty()); 1.126 + NS_ENSURE_ARG_POINTER(aURI); 1.127 + 1.128 + nsresult rv; 1.129 + *aURI = nullptr; 1.130 + 1.131 + nsAutoCString uriString(aStringURI); 1.132 + uriString.Trim(" "); // Cleanup the empty spaces that might be on each end. 1.133 + 1.134 + // Eliminate embedded newlines, which single-line text fields now allow: 1.135 + uriString.StripChars("\r\n"); 1.136 + 1.137 + NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE); 1.138 + 1.139 + nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); 1.140 + NS_ENSURE_SUCCESS(rv, rv); 1.141 + nsAutoCString scheme; 1.142 + ioService->ExtractScheme(aStringURI, scheme); 1.143 + 1.144 + // View-source is a pseudo scheme. We're interested in fixing up the stuff 1.145 + // after it. The easiest way to do that is to call this method again with the 1.146 + // "view-source:" lopped off and then prepend it again afterwards. 1.147 + 1.148 + if (scheme.LowerCaseEqualsLiteral("view-source")) 1.149 + { 1.150 + nsCOMPtr<nsIURI> uri; 1.151 + uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; 1.152 + 1.153 + rv = CreateFixupURI(Substring(uriString, 1.154 + sizeof("view-source:") - 1, 1.155 + uriString.Length() - 1.156 + (sizeof("view-source:") - 1)), 1.157 + newFixupFlags, aPostData, getter_AddRefs(uri)); 1.158 + if (NS_FAILED(rv)) 1.159 + return NS_ERROR_FAILURE; 1.160 + nsAutoCString spec; 1.161 + uri->GetSpec(spec); 1.162 + uriString.Assign(NS_LITERAL_CSTRING("view-source:") + spec); 1.163 + } 1.164 + else { 1.165 + // Check for if it is a file URL 1.166 + FileURIFixup(uriString, aURI); 1.167 + if(*aURI) 1.168 + return NS_OK; 1.169 + 1.170 +#if defined(XP_WIN) 1.171 + // Not a file URL, so translate '\' to '/' for convenience in the common protocols 1.172 + // e.g. catch 1.173 + // 1.174 + // http:\\broken.com\address 1.175 + // http:\\broken.com/blah 1.176 + // broken.com\blah 1.177 + // 1.178 + // Code will also do partial fix up the following urls 1.179 + // 1.180 + // http:\\broken.com\address/somewhere\image.jpg (stops at first forward slash) 1.181 + // http:\\broken.com\blah?arg=somearg\foo.jpg (stops at question mark) 1.182 + // http:\\broken.com#odd\ref (stops at hash) 1.183 + // 1.184 + if (scheme.IsEmpty() || 1.185 + scheme.LowerCaseEqualsLiteral("http") || 1.186 + scheme.LowerCaseEqualsLiteral("https") || 1.187 + scheme.LowerCaseEqualsLiteral("ftp")) 1.188 + { 1.189 + // Walk the string replacing backslashes with forward slashes until 1.190 + // the end is reached, or a question mark, or a hash, or a forward 1.191 + // slash. The forward slash test is to stop before trampling over 1.192 + // URIs which legitimately contain a mix of both forward and 1.193 + // backward slashes. 1.194 + nsAutoCString::iterator start; 1.195 + nsAutoCString::iterator end; 1.196 + uriString.BeginWriting(start); 1.197 + uriString.EndWriting(end); 1.198 + while (start != end) { 1.199 + if (*start == '?' || *start == '#' || *start == '/') 1.200 + break; 1.201 + if (*start == '\\') 1.202 + *start = '/'; 1.203 + ++start; 1.204 + } 1.205 + } 1.206 +#endif 1.207 + } 1.208 + 1.209 + if (!sInitializedPrefCaches) { 1.210 + // Check if we want to fix up common scheme typos. 1.211 + rv = Preferences::AddBoolVarCache(&sFixTypos, 1.212 + "browser.fixup.typo.scheme", 1.213 + sFixTypos); 1.214 + MOZ_ASSERT(NS_SUCCEEDED(rv), 1.215 + "Failed to observe \"browser.fixup.typo.scheme\""); 1.216 + sInitializedPrefCaches = true; 1.217 + } 1.218 + 1.219 + // Fix up common scheme typos. 1.220 + if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) { 1.221 + 1.222 + // Fast-path for common cases. 1.223 + if (scheme.IsEmpty() || 1.224 + scheme.LowerCaseEqualsLiteral("http") || 1.225 + scheme.LowerCaseEqualsLiteral("https") || 1.226 + scheme.LowerCaseEqualsLiteral("ftp") || 1.227 + scheme.LowerCaseEqualsLiteral("file")) { 1.228 + // Do nothing. 1.229 + } else if (scheme.LowerCaseEqualsLiteral("ttp")) { 1.230 + // ttp -> http. 1.231 + uriString.Replace(0, 3, "http"); 1.232 + scheme.AssignLiteral("http"); 1.233 + } else if (scheme.LowerCaseEqualsLiteral("ttps")) { 1.234 + // ttps -> https. 1.235 + uriString.Replace(0, 4, "https"); 1.236 + scheme.AssignLiteral("https"); 1.237 + } else if (scheme.LowerCaseEqualsLiteral("tps")) { 1.238 + // tps -> https. 1.239 + uriString.Replace(0, 3, "https"); 1.240 + scheme.AssignLiteral("https"); 1.241 + } else if (scheme.LowerCaseEqualsLiteral("ps")) { 1.242 + // ps -> https. 1.243 + uriString.Replace(0, 2, "https"); 1.244 + scheme.AssignLiteral("https"); 1.245 + } else if (scheme.LowerCaseEqualsLiteral("ile")) { 1.246 + // ile -> file. 1.247 + uriString.Replace(0, 3, "file"); 1.248 + scheme.AssignLiteral("file"); 1.249 + } else if (scheme.LowerCaseEqualsLiteral("le")) { 1.250 + // le -> file. 1.251 + uriString.Replace(0, 2, "file"); 1.252 + scheme.AssignLiteral("file"); 1.253 + } 1.254 + } 1.255 + 1.256 + // Now we need to check whether "scheme" is something we don't 1.257 + // really know about. 1.258 + nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler; 1.259 + 1.260 + ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler)); 1.261 + extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default"); 1.262 + 1.263 + if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) { 1.264 + // Just try to create an URL out of it 1.265 + rv = NS_NewURI(aURI, uriString, nullptr); 1.266 + 1.267 + if (!*aURI && rv != NS_ERROR_MALFORMED_URI) { 1.268 + return rv; 1.269 + } 1.270 + } 1.271 + 1.272 + if (*aURI) { 1.273 + if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) 1.274 + MakeAlternateURI(*aURI); 1.275 + return NS_OK; 1.276 + } 1.277 + 1.278 + // See if it is a keyword 1.279 + // Test whether keywords need to be fixed up 1.280 + bool fixupKeywords = false; 1.281 + if (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) { 1.282 + nsresult rv = Preferences::GetBool("keyword.enabled", &fixupKeywords); 1.283 + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1.284 + if (fixupKeywords) 1.285 + { 1.286 + KeywordURIFixup(uriString, aPostData, aURI); 1.287 + if(*aURI) 1.288 + return NS_OK; 1.289 + } 1.290 + } 1.291 + 1.292 + // Prune duff protocol schemes 1.293 + // 1.294 + // ://totallybroken.url.com 1.295 + // //shorthand.url.com 1.296 + // 1.297 + if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) 1.298 + { 1.299 + uriString = StringTail(uriString, uriString.Length() - 3); 1.300 + } 1.301 + else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) 1.302 + { 1.303 + uriString = StringTail(uriString, uriString.Length() - 2); 1.304 + } 1.305 + 1.306 + // Add ftp:// or http:// to front of url if it has no spec 1.307 + // 1.308 + // Should fix: 1.309 + // 1.310 + // no-scheme.com 1.311 + // ftp.no-scheme.com 1.312 + // ftp4.no-scheme.com 1.313 + // no-scheme.com/query?foo=http://www.foo.com 1.314 + // 1.315 + int32_t schemeDelim = uriString.Find("://",0); 1.316 + int32_t firstDelim = uriString.FindCharInSet("/:"); 1.317 + if (schemeDelim <= 0 || 1.318 + (firstDelim != -1 && schemeDelim > firstDelim)) { 1.319 + // find host name 1.320 + int32_t hostPos = uriString.FindCharInSet("/:?#"); 1.321 + if (hostPos == -1) 1.322 + hostPos = uriString.Length(); 1.323 + 1.324 + // extract host name 1.325 + nsAutoCString hostSpec; 1.326 + uriString.Left(hostSpec, hostPos); 1.327 + 1.328 + // insert url spec corresponding to host name 1.329 + if (IsLikelyFTP(hostSpec)) 1.330 + uriString.Assign(NS_LITERAL_CSTRING("ftp://") + uriString); 1.331 + else 1.332 + uriString.Assign(NS_LITERAL_CSTRING("http://") + uriString); 1.333 + } // end if checkprotocol 1.334 + 1.335 + rv = NS_NewURI(aURI, uriString, nullptr); 1.336 + 1.337 + // Did the caller want us to try an alternative URI? 1.338 + // If so, attempt to fixup http://foo into http://www.foo.com 1.339 + 1.340 + if (*aURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) { 1.341 + MakeAlternateURI(*aURI); 1.342 + } 1.343 + 1.344 + // If we still haven't been able to construct a valid URI, try to force a 1.345 + // keyword match. This catches search strings with '.' or ':' in them. 1.346 + if (!*aURI && fixupKeywords) 1.347 + { 1.348 + KeywordToURI(aStringURI, aPostData, aURI); 1.349 + if(*aURI) 1.350 + return NS_OK; 1.351 + } 1.352 + 1.353 + return rv; 1.354 +} 1.355 + 1.356 +NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword, 1.357 + nsIInputStream **aPostData, 1.358 + nsIURI **aURI) 1.359 +{ 1.360 + *aURI = nullptr; 1.361 + if (aPostData) { 1.362 + *aPostData = nullptr; 1.363 + } 1.364 + NS_ENSURE_STATE(Preferences::GetRootBranch()); 1.365 + 1.366 + // Strip leading "?" and leading/trailing spaces from aKeyword 1.367 + nsAutoCString keyword(aKeyword); 1.368 + if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) { 1.369 + keyword.Cut(0, 1); 1.370 + } 1.371 + keyword.Trim(" "); 1.372 + 1.373 + if (XRE_GetProcessType() == GeckoProcessType_Content) { 1.374 + dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); 1.375 + if (!contentChild) { 1.376 + return NS_ERROR_NOT_AVAILABLE; 1.377 + } 1.378 + 1.379 + ipc::OptionalInputStreamParams postData; 1.380 + ipc::OptionalURIParams uri; 1.381 + if (!contentChild->SendKeywordToURI(keyword, &postData, &uri)) { 1.382 + return NS_ERROR_FAILURE; 1.383 + } 1.384 + 1.385 + if (aPostData) { 1.386 + nsTArray<ipc::FileDescriptor> fds; 1.387 + nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds); 1.388 + temp.forget(aPostData); 1.389 + 1.390 + MOZ_ASSERT(fds.IsEmpty()); 1.391 + } 1.392 + 1.393 + nsCOMPtr<nsIURI> temp = DeserializeURI(uri); 1.394 + temp.forget(aURI); 1.395 + return NS_OK; 1.396 + } 1.397 + 1.398 +#ifdef MOZ_TOOLKIT_SEARCH 1.399 + // Try falling back to the search service's default search engine 1.400 + nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1"); 1.401 + if (searchSvc) { 1.402 + nsCOMPtr<nsISearchEngine> defaultEngine; 1.403 + searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine)); 1.404 + if (defaultEngine) { 1.405 + nsCOMPtr<nsISearchSubmission> submission; 1.406 + // We allow default search plugins to specify alternate 1.407 + // parameters that are specific to keyword searches. 1.408 + NS_NAMED_LITERAL_STRING(mozKeywordSearch, "application/x-moz-keywordsearch"); 1.409 + bool supportsResponseType = false; 1.410 + defaultEngine->SupportsResponseType(mozKeywordSearch, &supportsResponseType); 1.411 + if (supportsResponseType) 1.412 + defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword), 1.413 + mozKeywordSearch, 1.414 + NS_LITERAL_STRING("keyword"), 1.415 + getter_AddRefs(submission)); 1.416 + else 1.417 + defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword), 1.418 + EmptyString(), 1.419 + NS_LITERAL_STRING("keyword"), 1.420 + getter_AddRefs(submission)); 1.421 + if (submission) { 1.422 + nsCOMPtr<nsIInputStream> postData; 1.423 + submission->GetPostData(getter_AddRefs(postData)); 1.424 + if (aPostData) { 1.425 + postData.forget(aPostData); 1.426 + } else if (postData) { 1.427 + // The submission specifies POST data (i.e. the search 1.428 + // engine's "method" is POST), but our caller didn't allow 1.429 + // passing post data back. No point passing back a URL that 1.430 + // won't load properly. 1.431 + return NS_ERROR_FAILURE; 1.432 + } 1.433 + 1.434 + // This notification is meant for Firefox Health Report so it 1.435 + // can increment counts from the search engine. The assumption 1.436 + // here is that this keyword/submission will eventually result 1.437 + // in a search. Since we only generate a URI here, there is the 1.438 + // possibility we'll increment the counter without actually 1.439 + // incurring a search. A robust solution would involve currying 1.440 + // the search engine's name through various function calls. 1.441 + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); 1.442 + if (obsSvc) { 1.443 + // Note that "keyword-search" refers to a search via the url 1.444 + // bar, not a bookmarks keyword search. 1.445 + obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get()); 1.446 + } 1.447 + 1.448 + return submission->GetUri(aURI); 1.449 + } 1.450 + } 1.451 + } 1.452 +#endif 1.453 + 1.454 + // out of options 1.455 + return NS_ERROR_NOT_AVAILABLE; 1.456 +} 1.457 + 1.458 +bool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI) 1.459 +{ 1.460 + if (!Preferences::GetRootBranch()) 1.461 + { 1.462 + return false; 1.463 + } 1.464 + if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) 1.465 + { 1.466 + return false; 1.467 + } 1.468 + 1.469 + // Code only works for http. Not for any other protocol including https! 1.470 + bool isHttp = false; 1.471 + aURI->SchemeIs("http", &isHttp); 1.472 + if (!isHttp) { 1.473 + return false; 1.474 + } 1.475 + 1.476 + // Security - URLs with user / password info should NOT be fixed up 1.477 + nsAutoCString userpass; 1.478 + aURI->GetUserPass(userpass); 1.479 + if (!userpass.IsEmpty()) { 1.480 + return false; 1.481 + } 1.482 + 1.483 + nsAutoCString oldHost; 1.484 + nsAutoCString newHost; 1.485 + aURI->GetHost(oldHost); 1.486 + 1.487 + // Count the dots 1.488 + int32_t numDots = 0; 1.489 + nsReadingIterator<char> iter; 1.490 + nsReadingIterator<char> iterEnd; 1.491 + oldHost.BeginReading(iter); 1.492 + oldHost.EndReading(iterEnd); 1.493 + while (iter != iterEnd) { 1.494 + if (*iter == '.') 1.495 + numDots++; 1.496 + ++iter; 1.497 + } 1.498 + 1.499 + 1.500 + // Get the prefix and suffix to stick onto the new hostname. By default these 1.501 + // are www. & .com but they could be any other value, e.g. www. & .org 1.502 + 1.503 + nsAutoCString prefix("www."); 1.504 + nsAdoptingCString prefPrefix = 1.505 + Preferences::GetCString("browser.fixup.alternate.prefix"); 1.506 + if (prefPrefix) 1.507 + { 1.508 + prefix.Assign(prefPrefix); 1.509 + } 1.510 + 1.511 + nsAutoCString suffix(".com"); 1.512 + nsAdoptingCString prefSuffix = 1.513 + Preferences::GetCString("browser.fixup.alternate.suffix"); 1.514 + if (prefSuffix) 1.515 + { 1.516 + suffix.Assign(prefSuffix); 1.517 + } 1.518 + 1.519 + if (numDots == 0) 1.520 + { 1.521 + newHost.Assign(prefix); 1.522 + newHost.Append(oldHost); 1.523 + newHost.Append(suffix); 1.524 + } 1.525 + else if (numDots == 1) 1.526 + { 1.527 + if (!prefix.IsEmpty() && 1.528 + oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) { 1.529 + newHost.Assign(oldHost); 1.530 + newHost.Append(suffix); 1.531 + } 1.532 + else if (!suffix.IsEmpty()) { 1.533 + newHost.Assign(prefix); 1.534 + newHost.Append(oldHost); 1.535 + } 1.536 + else 1.537 + { 1.538 + // Do nothing 1.539 + return false; 1.540 + } 1.541 + } 1.542 + else 1.543 + { 1.544 + // Do nothing 1.545 + return false; 1.546 + } 1.547 + 1.548 + if (newHost.IsEmpty()) { 1.549 + return false; 1.550 + } 1.551 + 1.552 + // Assign the new host string over the old one 1.553 + aURI->SetHost(newHost); 1.554 + return true; 1.555 +} 1.556 + 1.557 +/** 1.558 + * Check if the host name starts with ftp\d*\. and it's not directly followed 1.559 + * by the tld. 1.560 + */ 1.561 +bool nsDefaultURIFixup::IsLikelyFTP(const nsCString &aHostSpec) 1.562 +{ 1.563 + bool likelyFTP = false; 1.564 + if (aHostSpec.EqualsIgnoreCase("ftp", 3)) { 1.565 + nsACString::const_iterator iter; 1.566 + nsACString::const_iterator end; 1.567 + aHostSpec.BeginReading(iter); 1.568 + aHostSpec.EndReading(end); 1.569 + iter.advance(3); // move past the "ftp" part 1.570 + 1.571 + while (iter != end) 1.572 + { 1.573 + if (*iter == '.') { 1.574 + // now make sure the name has at least one more dot in it 1.575 + ++iter; 1.576 + while (iter != end) 1.577 + { 1.578 + if (*iter == '.') { 1.579 + likelyFTP = true; 1.580 + break; 1.581 + } 1.582 + ++iter; 1.583 + } 1.584 + break; 1.585 + } 1.586 + else if (!nsCRT::IsAsciiDigit(*iter)) { 1.587 + break; 1.588 + } 1.589 + ++iter; 1.590 + } 1.591 + } 1.592 + return likelyFTP; 1.593 +} 1.594 + 1.595 +nsresult nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI, 1.596 + nsIURI** aURI) 1.597 +{ 1.598 + nsAutoCString uriSpecOut; 1.599 + 1.600 + nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut); 1.601 + if (NS_SUCCEEDED(rv)) 1.602 + { 1.603 + // if this is file url, uriSpecOut is already in FS charset 1.604 + if(NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr))) 1.605 + return NS_OK; 1.606 + } 1.607 + return NS_ERROR_FAILURE; 1.608 +} 1.609 + 1.610 +nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn, 1.611 + nsCString& aOut) 1.612 +{ 1.613 + bool attemptFixup = false; 1.614 + 1.615 +#if defined(XP_WIN) 1.616 + // Check for \ in the url-string or just a drive (PC) 1.617 + if(kNotFound != aIn.FindChar('\\') || 1.618 + (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) 1.619 + { 1.620 + attemptFixup = true; 1.621 + } 1.622 +#elif defined(XP_UNIX) 1.623 + // Check if it starts with / (UNIX) 1.624 + if(aIn.First() == '/') 1.625 + { 1.626 + attemptFixup = true; 1.627 + } 1.628 +#else 1.629 + // Do nothing (All others for now) 1.630 +#endif 1.631 + 1.632 + if (attemptFixup) 1.633 + { 1.634 + // Test if this is a valid path by trying to create a local file 1.635 + // object. The URL of that is returned if successful. 1.636 + 1.637 + // NOTE: Please be sure to check that the call to NS_NewLocalFile 1.638 + // rejects bad file paths when using this code on a new 1.639 + // platform. 1.640 + 1.641 + nsCOMPtr<nsIFile> filePath; 1.642 + nsresult rv; 1.643 + 1.644 + // this is not the real fix but a temporary fix 1.645 + // in order to really fix the problem, we need to change the 1.646 + // nsICmdLineService interface to use wstring to pass paramenters 1.647 + // instead of string since path name and other argument could be 1.648 + // in non ascii.(see bug 87127) Since it is too risky to make interface change right 1.649 + // now, we decide not to do so now. 1.650 + // Therefore, the aIn we receive here maybe already in damage form 1.651 + // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t 1.652 + // while the real data could be in file system charset ) 1.653 + // we choice the following logic which will work for most of the case. 1.654 + // Case will still failed only if it meet ALL the following condiction: 1.655 + // 1. running on CJK, Russian, or Greek system, and 1.656 + // 2. user type it from URL bar 1.657 + // 3. the file name contains character in the range of 1.658 + // U+00A1-U+00FF but encode as different code point in file 1.659 + // system charset (e.g. ACP on window)- this is very rare case 1.660 + // We should remove this logic and convert to File system charset here 1.661 + // once we change nsICmdLineService to use wstring and ensure 1.662 + // all the Unicode data come in is correctly converted. 1.663 + // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases 1.664 + // what we have is actually a "utf8" version of a "utf16" string that's 1.665 + // actually byte-expanded native-encoding data. Someone upstream needs 1.666 + // to stop using AssignWithConversion and do things correctly. See bug 1.667 + // 58866 for what happens if we remove this 1.668 + // PossiblyByteExpandedFileName check. 1.669 + NS_ConvertUTF8toUTF16 in(aIn); 1.670 + if (PossiblyByteExpandedFileName(in)) { 1.671 + // removes high byte 1.672 + rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false, getter_AddRefs(filePath)); 1.673 + } 1.674 + else { 1.675 + // input is unicode 1.676 + rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath)); 1.677 + } 1.678 + 1.679 + if (NS_SUCCEEDED(rv)) 1.680 + { 1.681 + NS_GetURLSpecFromFile(filePath, aOut); 1.682 + return NS_OK; 1.683 + } 1.684 + } 1.685 + 1.686 + return NS_ERROR_FAILURE; 1.687 +} 1.688 + 1.689 +bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl) 1.690 +{ 1.691 + // Oh dear, the protocol is invalid. Test if the protocol might 1.692 + // actually be a url without a protocol: 1.693 + // 1.694 + // http://www.faqs.org/rfcs/rfc1738.html 1.695 + // http://www.faqs.org/rfcs/rfc2396.html 1.696 + // 1.697 + // e.g. Anything of the form: 1.698 + // 1.699 + // <hostname>:<port> or 1.700 + // <hostname>:<port>/ 1.701 + // 1.702 + // Where <hostname> is a string of alphanumeric characters and dashes 1.703 + // separated by dots. 1.704 + // and <port> is a 5 or less digits. This actually breaks the rfc2396 1.705 + // definition of a scheme which allows dots in schemes. 1.706 + // 1.707 + // Note: 1.708 + // People expecting this to work with 1.709 + // <user>:<password>@<host>:<port>/<url-path> will be disappointed! 1.710 + // 1.711 + // Note: Parser could be a lot tighter, tossing out silly hostnames 1.712 + // such as those containing consecutive dots and so on. 1.713 + 1.714 + // Read the hostname which should of the form 1.715 + // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*: 1.716 + 1.717 + nsACString::const_iterator iterBegin; 1.718 + nsACString::const_iterator iterEnd; 1.719 + aUrl.BeginReading(iterBegin); 1.720 + aUrl.EndReading(iterEnd); 1.721 + nsACString::const_iterator iter = iterBegin; 1.722 + 1.723 + while (iter != iterEnd) 1.724 + { 1.725 + uint32_t chunkSize = 0; 1.726 + // Parse a chunk of the address 1.727 + while (iter != iterEnd && 1.728 + (*iter == '-' || 1.729 + nsCRT::IsAsciiAlpha(*iter) || 1.730 + nsCRT::IsAsciiDigit(*iter))) 1.731 + { 1.732 + ++chunkSize; 1.733 + ++iter; 1.734 + } 1.735 + if (chunkSize == 0 || iter == iterEnd) 1.736 + { 1.737 + return false; 1.738 + } 1.739 + if (*iter == ':') 1.740 + { 1.741 + // Go onto checking the for the digits 1.742 + break; 1.743 + } 1.744 + if (*iter != '.') 1.745 + { 1.746 + // Whatever it is, it ain't a hostname! 1.747 + return false; 1.748 + } 1.749 + ++iter; 1.750 + } 1.751 + if (iter == iterEnd) 1.752 + { 1.753 + // No point continuing since there is no colon 1.754 + return false; 1.755 + } 1.756 + ++iter; 1.757 + 1.758 + // Count the number of digits after the colon and before the 1.759 + // next forward slash (or end of string) 1.760 + 1.761 + uint32_t digitCount = 0; 1.762 + while (iter != iterEnd && digitCount <= 5) 1.763 + { 1.764 + if (nsCRT::IsAsciiDigit(*iter)) 1.765 + { 1.766 + digitCount++; 1.767 + } 1.768 + else if (*iter == '/') 1.769 + { 1.770 + break; 1.771 + } 1.772 + else 1.773 + { 1.774 + // Whatever it is, it ain't a port! 1.775 + return false; 1.776 + } 1.777 + ++iter; 1.778 + } 1.779 + if (digitCount == 0 || digitCount > 5) 1.780 + { 1.781 + // No digits or more digits than a port would have. 1.782 + return false; 1.783 + } 1.784 + 1.785 + // Yes, it's possibly a host:port url 1.786 + return true; 1.787 +} 1.788 + 1.789 +bool nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn) 1.790 +{ 1.791 + // XXXXX HACK XXXXX : please don't copy this code. 1.792 + // There are cases where aIn contains the locale byte chars padded to short 1.793 + // (thus the name "ByteExpanded"); whereas other cases 1.794 + // have proper Unicode code points. 1.795 + // This is a temporary fix. Please refer to 58866, 86948 1.796 + 1.797 + nsReadingIterator<char16_t> iter; 1.798 + nsReadingIterator<char16_t> iterEnd; 1.799 + aIn.BeginReading(iter); 1.800 + aIn.EndReading(iterEnd); 1.801 + while (iter != iterEnd) 1.802 + { 1.803 + if (*iter >= 0x0080 && *iter <= 0x00FF) 1.804 + return true; 1.805 + ++iter; 1.806 + } 1.807 + return false; 1.808 +} 1.809 + 1.810 +void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString, 1.811 + nsIInputStream **aPostData, 1.812 + nsIURI** aURI) 1.813 +{ 1.814 + // These are keyword formatted strings 1.815 + // "what is mozilla" 1.816 + // "what is mozilla?" 1.817 + // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring 1.818 + // "?mozilla" - anything that begins with a question mark 1.819 + // "?site:mozilla.org docshell" 1.820 + // Things that have a quote before the first dot/colon 1.821 + 1.822 + // These are not keyword formatted strings 1.823 + // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?" 1.824 + // "www.blah.com stuff" 1.825 + // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?" 1.826 + // "nonQualifiedHost:80 args" 1.827 + // "nonQualifiedHost?" 1.828 + // "nonQualifiedHost?args" 1.829 + // "nonQualifiedHost?some args" 1.830 + 1.831 + // Note: uint32_t(kNotFound) is greater than any actual location 1.832 + // in practice. So if we cast all locations to uint32_t, then a < 1.833 + // b guarantees that either b is kNotFound and a is found, or both 1.834 + // are found and a found before b. 1.835 + uint32_t dotLoc = uint32_t(aURIString.FindChar('.')); 1.836 + uint32_t colonLoc = uint32_t(aURIString.FindChar(':')); 1.837 + uint32_t spaceLoc = uint32_t(aURIString.FindChar(' ')); 1.838 + if (spaceLoc == 0) { 1.839 + // Treat this as not found 1.840 + spaceLoc = uint32_t(kNotFound); 1.841 + } 1.842 + uint32_t qMarkLoc = uint32_t(aURIString.FindChar('?')); 1.843 + uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')), 1.844 + uint32_t(aURIString.FindChar('\''))); 1.845 + 1.846 + if (((spaceLoc < dotLoc || quoteLoc < dotLoc) && 1.847 + (spaceLoc < colonLoc || quoteLoc < colonLoc) && 1.848 + (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) || 1.849 + qMarkLoc == 0) 1.850 + { 1.851 + KeywordToURI(aURIString, aPostData, aURI); 1.852 + } 1.853 +} 1.854 + 1.855 + 1.856 +nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup) 1.857 +{ 1.858 + nsDefaultURIFixup *fixup = new nsDefaultURIFixup; 1.859 + if (fixup == nullptr) 1.860 + { 1.861 + return NS_ERROR_OUT_OF_MEMORY; 1.862 + } 1.863 + return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup); 1.864 +} 1.865 +