docshell/base/nsDefaultURIFixup.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsNetUtil.h"
michael@0 8 #include "nsCRT.h"
michael@0 9
michael@0 10 #include "nsIFile.h"
michael@0 11 #include <algorithm>
michael@0 12
michael@0 13 #ifdef MOZ_TOOLKIT_SEARCH
michael@0 14 #include "nsIBrowserSearchService.h"
michael@0 15 #endif
michael@0 16
michael@0 17 #include "nsIURIFixup.h"
michael@0 18 #include "nsDefaultURIFixup.h"
michael@0 19 #include "mozilla/Preferences.h"
michael@0 20 #include "mozilla/dom/ContentChild.h"
michael@0 21 #include "mozilla/ipc/InputStreamUtils.h"
michael@0 22 #include "mozilla/ipc/URIUtils.h"
michael@0 23 #include "nsIObserverService.h"
michael@0 24 #include "nsXULAppAPI.h"
michael@0 25
michael@0 26 using namespace mozilla;
michael@0 27
michael@0 28 /* Implementation file */
michael@0 29 NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
michael@0 30
michael@0 31 static bool sInitializedPrefCaches = false;
michael@0 32 static bool sFixTypos = true;
michael@0 33
michael@0 34 nsDefaultURIFixup::nsDefaultURIFixup()
michael@0 35 {
michael@0 36 /* member initializers and constructor code */
michael@0 37 }
michael@0 38
michael@0 39
michael@0 40 nsDefaultURIFixup::~nsDefaultURIFixup()
michael@0 41 {
michael@0 42 /* destructor code */
michael@0 43 }
michael@0 44
michael@0 45 /* nsIURI createExposableURI (in nsIURI aURI); */
michael@0 46 NS_IMETHODIMP
michael@0 47 nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn)
michael@0 48 {
michael@0 49 NS_ENSURE_ARG_POINTER(aURI);
michael@0 50 NS_ENSURE_ARG_POINTER(aReturn);
michael@0 51
michael@0 52 bool isWyciwyg = false;
michael@0 53 aURI->SchemeIs("wyciwyg", &isWyciwyg);
michael@0 54
michael@0 55 nsAutoCString userPass;
michael@0 56 aURI->GetUserPass(userPass);
michael@0 57
michael@0 58 // most of the time we can just AddRef and return
michael@0 59 if (!isWyciwyg && userPass.IsEmpty())
michael@0 60 {
michael@0 61 *aReturn = aURI;
michael@0 62 NS_ADDREF(*aReturn);
michael@0 63 return NS_OK;
michael@0 64 }
michael@0 65
michael@0 66 // Rats, we have to massage the URI
michael@0 67 nsCOMPtr<nsIURI> uri;
michael@0 68 if (isWyciwyg)
michael@0 69 {
michael@0 70 nsAutoCString path;
michael@0 71 nsresult rv = aURI->GetPath(path);
michael@0 72 NS_ENSURE_SUCCESS(rv, rv);
michael@0 73
michael@0 74 uint32_t pathLength = path.Length();
michael@0 75 if (pathLength <= 2)
michael@0 76 {
michael@0 77 return NS_ERROR_FAILURE;
michael@0 78 }
michael@0 79
michael@0 80 // Path is of the form "//123/http://foo/bar", with a variable number of digits.
michael@0 81 // To figure out where the "real" URL starts, search path for a '/', starting at
michael@0 82 // the third character.
michael@0 83 int32_t slashIndex = path.FindChar('/', 2);
michael@0 84 if (slashIndex == kNotFound)
michael@0 85 {
michael@0 86 return NS_ERROR_FAILURE;
michael@0 87 }
michael@0 88
michael@0 89 // Get the charset of the original URI so we can pass it to our fixed up URI.
michael@0 90 nsAutoCString charset;
michael@0 91 aURI->GetOriginCharset(charset);
michael@0 92
michael@0 93 rv = NS_NewURI(getter_AddRefs(uri),
michael@0 94 Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
michael@0 95 charset.get());
michael@0 96 NS_ENSURE_SUCCESS(rv, rv);
michael@0 97 }
michael@0 98 else
michael@0 99 {
michael@0 100 // clone the URI so zapping user:pass doesn't change the original
michael@0 101 nsresult rv = aURI->Clone(getter_AddRefs(uri));
michael@0 102 NS_ENSURE_SUCCESS(rv, rv);
michael@0 103 }
michael@0 104
michael@0 105 // hide user:pass unless overridden by pref
michael@0 106 if (Preferences::GetBool("browser.fixup.hide_user_pass", true))
michael@0 107 {
michael@0 108 uri->SetUserPass(EmptyCString());
michael@0 109 }
michael@0 110
michael@0 111 // return the fixed-up URI
michael@0 112 *aReturn = uri;
michael@0 113 NS_ADDREF(*aReturn);
michael@0 114 return NS_OK;
michael@0 115 }
michael@0 116
michael@0 117 /* nsIURI createFixupURI (in nsAUTF8String aURIText, in unsigned long aFixupFlags); */
michael@0 118 NS_IMETHODIMP
michael@0 119 nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags,
michael@0 120 nsIInputStream **aPostData, nsIURI **aURI)
michael@0 121 {
michael@0 122 NS_ENSURE_ARG(!aStringURI.IsEmpty());
michael@0 123 NS_ENSURE_ARG_POINTER(aURI);
michael@0 124
michael@0 125 nsresult rv;
michael@0 126 *aURI = nullptr;
michael@0 127
michael@0 128 nsAutoCString uriString(aStringURI);
michael@0 129 uriString.Trim(" "); // Cleanup the empty spaces that might be on each end.
michael@0 130
michael@0 131 // Eliminate embedded newlines, which single-line text fields now allow:
michael@0 132 uriString.StripChars("\r\n");
michael@0 133
michael@0 134 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
michael@0 135
michael@0 136 nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
michael@0 137 NS_ENSURE_SUCCESS(rv, rv);
michael@0 138 nsAutoCString scheme;
michael@0 139 ioService->ExtractScheme(aStringURI, scheme);
michael@0 140
michael@0 141 // View-source is a pseudo scheme. We're interested in fixing up the stuff
michael@0 142 // after it. The easiest way to do that is to call this method again with the
michael@0 143 // "view-source:" lopped off and then prepend it again afterwards.
michael@0 144
michael@0 145 if (scheme.LowerCaseEqualsLiteral("view-source"))
michael@0 146 {
michael@0 147 nsCOMPtr<nsIURI> uri;
michael@0 148 uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
michael@0 149
michael@0 150 rv = CreateFixupURI(Substring(uriString,
michael@0 151 sizeof("view-source:") - 1,
michael@0 152 uriString.Length() -
michael@0 153 (sizeof("view-source:") - 1)),
michael@0 154 newFixupFlags, aPostData, getter_AddRefs(uri));
michael@0 155 if (NS_FAILED(rv))
michael@0 156 return NS_ERROR_FAILURE;
michael@0 157 nsAutoCString spec;
michael@0 158 uri->GetSpec(spec);
michael@0 159 uriString.Assign(NS_LITERAL_CSTRING("view-source:") + spec);
michael@0 160 }
michael@0 161 else {
michael@0 162 // Check for if it is a file URL
michael@0 163 FileURIFixup(uriString, aURI);
michael@0 164 if(*aURI)
michael@0 165 return NS_OK;
michael@0 166
michael@0 167 #if defined(XP_WIN)
michael@0 168 // Not a file URL, so translate '\' to '/' for convenience in the common protocols
michael@0 169 // e.g. catch
michael@0 170 //
michael@0 171 // http:\\broken.com\address
michael@0 172 // http:\\broken.com/blah
michael@0 173 // broken.com\blah
michael@0 174 //
michael@0 175 // Code will also do partial fix up the following urls
michael@0 176 //
michael@0 177 // http:\\broken.com\address/somewhere\image.jpg (stops at first forward slash)
michael@0 178 // http:\\broken.com\blah?arg=somearg\foo.jpg (stops at question mark)
michael@0 179 // http:\\broken.com#odd\ref (stops at hash)
michael@0 180 //
michael@0 181 if (scheme.IsEmpty() ||
michael@0 182 scheme.LowerCaseEqualsLiteral("http") ||
michael@0 183 scheme.LowerCaseEqualsLiteral("https") ||
michael@0 184 scheme.LowerCaseEqualsLiteral("ftp"))
michael@0 185 {
michael@0 186 // Walk the string replacing backslashes with forward slashes until
michael@0 187 // the end is reached, or a question mark, or a hash, or a forward
michael@0 188 // slash. The forward slash test is to stop before trampling over
michael@0 189 // URIs which legitimately contain a mix of both forward and
michael@0 190 // backward slashes.
michael@0 191 nsAutoCString::iterator start;
michael@0 192 nsAutoCString::iterator end;
michael@0 193 uriString.BeginWriting(start);
michael@0 194 uriString.EndWriting(end);
michael@0 195 while (start != end) {
michael@0 196 if (*start == '?' || *start == '#' || *start == '/')
michael@0 197 break;
michael@0 198 if (*start == '\\')
michael@0 199 *start = '/';
michael@0 200 ++start;
michael@0 201 }
michael@0 202 }
michael@0 203 #endif
michael@0 204 }
michael@0 205
michael@0 206 if (!sInitializedPrefCaches) {
michael@0 207 // Check if we want to fix up common scheme typos.
michael@0 208 rv = Preferences::AddBoolVarCache(&sFixTypos,
michael@0 209 "browser.fixup.typo.scheme",
michael@0 210 sFixTypos);
michael@0 211 MOZ_ASSERT(NS_SUCCEEDED(rv),
michael@0 212 "Failed to observe \"browser.fixup.typo.scheme\"");
michael@0 213 sInitializedPrefCaches = true;
michael@0 214 }
michael@0 215
michael@0 216 // Fix up common scheme typos.
michael@0 217 if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
michael@0 218
michael@0 219 // Fast-path for common cases.
michael@0 220 if (scheme.IsEmpty() ||
michael@0 221 scheme.LowerCaseEqualsLiteral("http") ||
michael@0 222 scheme.LowerCaseEqualsLiteral("https") ||
michael@0 223 scheme.LowerCaseEqualsLiteral("ftp") ||
michael@0 224 scheme.LowerCaseEqualsLiteral("file")) {
michael@0 225 // Do nothing.
michael@0 226 } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
michael@0 227 // ttp -> http.
michael@0 228 uriString.Replace(0, 3, "http");
michael@0 229 scheme.AssignLiteral("http");
michael@0 230 } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
michael@0 231 // ttps -> https.
michael@0 232 uriString.Replace(0, 4, "https");
michael@0 233 scheme.AssignLiteral("https");
michael@0 234 } else if (scheme.LowerCaseEqualsLiteral("tps")) {
michael@0 235 // tps -> https.
michael@0 236 uriString.Replace(0, 3, "https");
michael@0 237 scheme.AssignLiteral("https");
michael@0 238 } else if (scheme.LowerCaseEqualsLiteral("ps")) {
michael@0 239 // ps -> https.
michael@0 240 uriString.Replace(0, 2, "https");
michael@0 241 scheme.AssignLiteral("https");
michael@0 242 } else if (scheme.LowerCaseEqualsLiteral("ile")) {
michael@0 243 // ile -> file.
michael@0 244 uriString.Replace(0, 3, "file");
michael@0 245 scheme.AssignLiteral("file");
michael@0 246 } else if (scheme.LowerCaseEqualsLiteral("le")) {
michael@0 247 // le -> file.
michael@0 248 uriString.Replace(0, 2, "file");
michael@0 249 scheme.AssignLiteral("file");
michael@0 250 }
michael@0 251 }
michael@0 252
michael@0 253 // Now we need to check whether "scheme" is something we don't
michael@0 254 // really know about.
michael@0 255 nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
michael@0 256
michael@0 257 ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
michael@0 258 extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default");
michael@0 259
michael@0 260 if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
michael@0 261 // Just try to create an URL out of it
michael@0 262 rv = NS_NewURI(aURI, uriString, nullptr);
michael@0 263
michael@0 264 if (!*aURI && rv != NS_ERROR_MALFORMED_URI) {
michael@0 265 return rv;
michael@0 266 }
michael@0 267 }
michael@0 268
michael@0 269 if (*aURI) {
michael@0 270 if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI)
michael@0 271 MakeAlternateURI(*aURI);
michael@0 272 return NS_OK;
michael@0 273 }
michael@0 274
michael@0 275 // See if it is a keyword
michael@0 276 // Test whether keywords need to be fixed up
michael@0 277 bool fixupKeywords = false;
michael@0 278 if (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) {
michael@0 279 nsresult rv = Preferences::GetBool("keyword.enabled", &fixupKeywords);
michael@0 280 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
michael@0 281 if (fixupKeywords)
michael@0 282 {
michael@0 283 KeywordURIFixup(uriString, aPostData, aURI);
michael@0 284 if(*aURI)
michael@0 285 return NS_OK;
michael@0 286 }
michael@0 287 }
michael@0 288
michael@0 289 // Prune duff protocol schemes
michael@0 290 //
michael@0 291 // ://totallybroken.url.com
michael@0 292 // //shorthand.url.com
michael@0 293 //
michael@0 294 if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
michael@0 295 {
michael@0 296 uriString = StringTail(uriString, uriString.Length() - 3);
michael@0 297 }
michael@0 298 else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//")))
michael@0 299 {
michael@0 300 uriString = StringTail(uriString, uriString.Length() - 2);
michael@0 301 }
michael@0 302
michael@0 303 // Add ftp:// or http:// to front of url if it has no spec
michael@0 304 //
michael@0 305 // Should fix:
michael@0 306 //
michael@0 307 // no-scheme.com
michael@0 308 // ftp.no-scheme.com
michael@0 309 // ftp4.no-scheme.com
michael@0 310 // no-scheme.com/query?foo=http://www.foo.com
michael@0 311 //
michael@0 312 int32_t schemeDelim = uriString.Find("://",0);
michael@0 313 int32_t firstDelim = uriString.FindCharInSet("/:");
michael@0 314 if (schemeDelim <= 0 ||
michael@0 315 (firstDelim != -1 && schemeDelim > firstDelim)) {
michael@0 316 // find host name
michael@0 317 int32_t hostPos = uriString.FindCharInSet("/:?#");
michael@0 318 if (hostPos == -1)
michael@0 319 hostPos = uriString.Length();
michael@0 320
michael@0 321 // extract host name
michael@0 322 nsAutoCString hostSpec;
michael@0 323 uriString.Left(hostSpec, hostPos);
michael@0 324
michael@0 325 // insert url spec corresponding to host name
michael@0 326 if (IsLikelyFTP(hostSpec))
michael@0 327 uriString.Assign(NS_LITERAL_CSTRING("ftp://") + uriString);
michael@0 328 else
michael@0 329 uriString.Assign(NS_LITERAL_CSTRING("http://") + uriString);
michael@0 330 } // end if checkprotocol
michael@0 331
michael@0 332 rv = NS_NewURI(aURI, uriString, nullptr);
michael@0 333
michael@0 334 // Did the caller want us to try an alternative URI?
michael@0 335 // If so, attempt to fixup http://foo into http://www.foo.com
michael@0 336
michael@0 337 if (*aURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
michael@0 338 MakeAlternateURI(*aURI);
michael@0 339 }
michael@0 340
michael@0 341 // If we still haven't been able to construct a valid URI, try to force a
michael@0 342 // keyword match. This catches search strings with '.' or ':' in them.
michael@0 343 if (!*aURI && fixupKeywords)
michael@0 344 {
michael@0 345 KeywordToURI(aStringURI, aPostData, aURI);
michael@0 346 if(*aURI)
michael@0 347 return NS_OK;
michael@0 348 }
michael@0 349
michael@0 350 return rv;
michael@0 351 }
michael@0 352
michael@0 353 NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
michael@0 354 nsIInputStream **aPostData,
michael@0 355 nsIURI **aURI)
michael@0 356 {
michael@0 357 *aURI = nullptr;
michael@0 358 if (aPostData) {
michael@0 359 *aPostData = nullptr;
michael@0 360 }
michael@0 361 NS_ENSURE_STATE(Preferences::GetRootBranch());
michael@0 362
michael@0 363 // Strip leading "?" and leading/trailing spaces from aKeyword
michael@0 364 nsAutoCString keyword(aKeyword);
michael@0 365 if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
michael@0 366 keyword.Cut(0, 1);
michael@0 367 }
michael@0 368 keyword.Trim(" ");
michael@0 369
michael@0 370 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 371 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
michael@0 372 if (!contentChild) {
michael@0 373 return NS_ERROR_NOT_AVAILABLE;
michael@0 374 }
michael@0 375
michael@0 376 ipc::OptionalInputStreamParams postData;
michael@0 377 ipc::OptionalURIParams uri;
michael@0 378 if (!contentChild->SendKeywordToURI(keyword, &postData, &uri)) {
michael@0 379 return NS_ERROR_FAILURE;
michael@0 380 }
michael@0 381
michael@0 382 if (aPostData) {
michael@0 383 nsTArray<ipc::FileDescriptor> fds;
michael@0 384 nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds);
michael@0 385 temp.forget(aPostData);
michael@0 386
michael@0 387 MOZ_ASSERT(fds.IsEmpty());
michael@0 388 }
michael@0 389
michael@0 390 nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
michael@0 391 temp.forget(aURI);
michael@0 392 return NS_OK;
michael@0 393 }
michael@0 394
michael@0 395 #ifdef MOZ_TOOLKIT_SEARCH
michael@0 396 // Try falling back to the search service's default search engine
michael@0 397 nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1");
michael@0 398 if (searchSvc) {
michael@0 399 nsCOMPtr<nsISearchEngine> defaultEngine;
michael@0 400 searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
michael@0 401 if (defaultEngine) {
michael@0 402 nsCOMPtr<nsISearchSubmission> submission;
michael@0 403 // We allow default search plugins to specify alternate
michael@0 404 // parameters that are specific to keyword searches.
michael@0 405 NS_NAMED_LITERAL_STRING(mozKeywordSearch, "application/x-moz-keywordsearch");
michael@0 406 bool supportsResponseType = false;
michael@0 407 defaultEngine->SupportsResponseType(mozKeywordSearch, &supportsResponseType);
michael@0 408 if (supportsResponseType)
michael@0 409 defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword),
michael@0 410 mozKeywordSearch,
michael@0 411 NS_LITERAL_STRING("keyword"),
michael@0 412 getter_AddRefs(submission));
michael@0 413 else
michael@0 414 defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword),
michael@0 415 EmptyString(),
michael@0 416 NS_LITERAL_STRING("keyword"),
michael@0 417 getter_AddRefs(submission));
michael@0 418 if (submission) {
michael@0 419 nsCOMPtr<nsIInputStream> postData;
michael@0 420 submission->GetPostData(getter_AddRefs(postData));
michael@0 421 if (aPostData) {
michael@0 422 postData.forget(aPostData);
michael@0 423 } else if (postData) {
michael@0 424 // The submission specifies POST data (i.e. the search
michael@0 425 // engine's "method" is POST), but our caller didn't allow
michael@0 426 // passing post data back. No point passing back a URL that
michael@0 427 // won't load properly.
michael@0 428 return NS_ERROR_FAILURE;
michael@0 429 }
michael@0 430
michael@0 431 // This notification is meant for Firefox Health Report so it
michael@0 432 // can increment counts from the search engine. The assumption
michael@0 433 // here is that this keyword/submission will eventually result
michael@0 434 // in a search. Since we only generate a URI here, there is the
michael@0 435 // possibility we'll increment the counter without actually
michael@0 436 // incurring a search. A robust solution would involve currying
michael@0 437 // the search engine's name through various function calls.
michael@0 438 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
michael@0 439 if (obsSvc) {
michael@0 440 // Note that "keyword-search" refers to a search via the url
michael@0 441 // bar, not a bookmarks keyword search.
michael@0 442 obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get());
michael@0 443 }
michael@0 444
michael@0 445 return submission->GetUri(aURI);
michael@0 446 }
michael@0 447 }
michael@0 448 }
michael@0 449 #endif
michael@0 450
michael@0 451 // out of options
michael@0 452 return NS_ERROR_NOT_AVAILABLE;
michael@0 453 }
michael@0 454
michael@0 455 bool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI)
michael@0 456 {
michael@0 457 if (!Preferences::GetRootBranch())
michael@0 458 {
michael@0 459 return false;
michael@0 460 }
michael@0 461 if (!Preferences::GetBool("browser.fixup.alternate.enabled", true))
michael@0 462 {
michael@0 463 return false;
michael@0 464 }
michael@0 465
michael@0 466 // Code only works for http. Not for any other protocol including https!
michael@0 467 bool isHttp = false;
michael@0 468 aURI->SchemeIs("http", &isHttp);
michael@0 469 if (!isHttp) {
michael@0 470 return false;
michael@0 471 }
michael@0 472
michael@0 473 // Security - URLs with user / password info should NOT be fixed up
michael@0 474 nsAutoCString userpass;
michael@0 475 aURI->GetUserPass(userpass);
michael@0 476 if (!userpass.IsEmpty()) {
michael@0 477 return false;
michael@0 478 }
michael@0 479
michael@0 480 nsAutoCString oldHost;
michael@0 481 nsAutoCString newHost;
michael@0 482 aURI->GetHost(oldHost);
michael@0 483
michael@0 484 // Count the dots
michael@0 485 int32_t numDots = 0;
michael@0 486 nsReadingIterator<char> iter;
michael@0 487 nsReadingIterator<char> iterEnd;
michael@0 488 oldHost.BeginReading(iter);
michael@0 489 oldHost.EndReading(iterEnd);
michael@0 490 while (iter != iterEnd) {
michael@0 491 if (*iter == '.')
michael@0 492 numDots++;
michael@0 493 ++iter;
michael@0 494 }
michael@0 495
michael@0 496
michael@0 497 // Get the prefix and suffix to stick onto the new hostname. By default these
michael@0 498 // are www. & .com but they could be any other value, e.g. www. & .org
michael@0 499
michael@0 500 nsAutoCString prefix("www.");
michael@0 501 nsAdoptingCString prefPrefix =
michael@0 502 Preferences::GetCString("browser.fixup.alternate.prefix");
michael@0 503 if (prefPrefix)
michael@0 504 {
michael@0 505 prefix.Assign(prefPrefix);
michael@0 506 }
michael@0 507
michael@0 508 nsAutoCString suffix(".com");
michael@0 509 nsAdoptingCString prefSuffix =
michael@0 510 Preferences::GetCString("browser.fixup.alternate.suffix");
michael@0 511 if (prefSuffix)
michael@0 512 {
michael@0 513 suffix.Assign(prefSuffix);
michael@0 514 }
michael@0 515
michael@0 516 if (numDots == 0)
michael@0 517 {
michael@0 518 newHost.Assign(prefix);
michael@0 519 newHost.Append(oldHost);
michael@0 520 newHost.Append(suffix);
michael@0 521 }
michael@0 522 else if (numDots == 1)
michael@0 523 {
michael@0 524 if (!prefix.IsEmpty() &&
michael@0 525 oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
michael@0 526 newHost.Assign(oldHost);
michael@0 527 newHost.Append(suffix);
michael@0 528 }
michael@0 529 else if (!suffix.IsEmpty()) {
michael@0 530 newHost.Assign(prefix);
michael@0 531 newHost.Append(oldHost);
michael@0 532 }
michael@0 533 else
michael@0 534 {
michael@0 535 // Do nothing
michael@0 536 return false;
michael@0 537 }
michael@0 538 }
michael@0 539 else
michael@0 540 {
michael@0 541 // Do nothing
michael@0 542 return false;
michael@0 543 }
michael@0 544
michael@0 545 if (newHost.IsEmpty()) {
michael@0 546 return false;
michael@0 547 }
michael@0 548
michael@0 549 // Assign the new host string over the old one
michael@0 550 aURI->SetHost(newHost);
michael@0 551 return true;
michael@0 552 }
michael@0 553
michael@0 554 /**
michael@0 555 * Check if the host name starts with ftp\d*\. and it's not directly followed
michael@0 556 * by the tld.
michael@0 557 */
michael@0 558 bool nsDefaultURIFixup::IsLikelyFTP(const nsCString &aHostSpec)
michael@0 559 {
michael@0 560 bool likelyFTP = false;
michael@0 561 if (aHostSpec.EqualsIgnoreCase("ftp", 3)) {
michael@0 562 nsACString::const_iterator iter;
michael@0 563 nsACString::const_iterator end;
michael@0 564 aHostSpec.BeginReading(iter);
michael@0 565 aHostSpec.EndReading(end);
michael@0 566 iter.advance(3); // move past the "ftp" part
michael@0 567
michael@0 568 while (iter != end)
michael@0 569 {
michael@0 570 if (*iter == '.') {
michael@0 571 // now make sure the name has at least one more dot in it
michael@0 572 ++iter;
michael@0 573 while (iter != end)
michael@0 574 {
michael@0 575 if (*iter == '.') {
michael@0 576 likelyFTP = true;
michael@0 577 break;
michael@0 578 }
michael@0 579 ++iter;
michael@0 580 }
michael@0 581 break;
michael@0 582 }
michael@0 583 else if (!nsCRT::IsAsciiDigit(*iter)) {
michael@0 584 break;
michael@0 585 }
michael@0 586 ++iter;
michael@0 587 }
michael@0 588 }
michael@0 589 return likelyFTP;
michael@0 590 }
michael@0 591
michael@0 592 nsresult nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI,
michael@0 593 nsIURI** aURI)
michael@0 594 {
michael@0 595 nsAutoCString uriSpecOut;
michael@0 596
michael@0 597 nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
michael@0 598 if (NS_SUCCEEDED(rv))
michael@0 599 {
michael@0 600 // if this is file url, uriSpecOut is already in FS charset
michael@0 601 if(NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr)))
michael@0 602 return NS_OK;
michael@0 603 }
michael@0 604 return NS_ERROR_FAILURE;
michael@0 605 }
michael@0 606
michael@0 607 nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
michael@0 608 nsCString& aOut)
michael@0 609 {
michael@0 610 bool attemptFixup = false;
michael@0 611
michael@0 612 #if defined(XP_WIN)
michael@0 613 // Check for \ in the url-string or just a drive (PC)
michael@0 614 if(kNotFound != aIn.FindChar('\\') ||
michael@0 615 (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|')))
michael@0 616 {
michael@0 617 attemptFixup = true;
michael@0 618 }
michael@0 619 #elif defined(XP_UNIX)
michael@0 620 // Check if it starts with / (UNIX)
michael@0 621 if(aIn.First() == '/')
michael@0 622 {
michael@0 623 attemptFixup = true;
michael@0 624 }
michael@0 625 #else
michael@0 626 // Do nothing (All others for now)
michael@0 627 #endif
michael@0 628
michael@0 629 if (attemptFixup)
michael@0 630 {
michael@0 631 // Test if this is a valid path by trying to create a local file
michael@0 632 // object. The URL of that is returned if successful.
michael@0 633
michael@0 634 // NOTE: Please be sure to check that the call to NS_NewLocalFile
michael@0 635 // rejects bad file paths when using this code on a new
michael@0 636 // platform.
michael@0 637
michael@0 638 nsCOMPtr<nsIFile> filePath;
michael@0 639 nsresult rv;
michael@0 640
michael@0 641 // this is not the real fix but a temporary fix
michael@0 642 // in order to really fix the problem, we need to change the
michael@0 643 // nsICmdLineService interface to use wstring to pass paramenters
michael@0 644 // instead of string since path name and other argument could be
michael@0 645 // in non ascii.(see bug 87127) Since it is too risky to make interface change right
michael@0 646 // now, we decide not to do so now.
michael@0 647 // Therefore, the aIn we receive here maybe already in damage form
michael@0 648 // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t
michael@0 649 // while the real data could be in file system charset )
michael@0 650 // we choice the following logic which will work for most of the case.
michael@0 651 // Case will still failed only if it meet ALL the following condiction:
michael@0 652 // 1. running on CJK, Russian, or Greek system, and
michael@0 653 // 2. user type it from URL bar
michael@0 654 // 3. the file name contains character in the range of
michael@0 655 // U+00A1-U+00FF but encode as different code point in file
michael@0 656 // system charset (e.g. ACP on window)- this is very rare case
michael@0 657 // We should remove this logic and convert to File system charset here
michael@0 658 // once we change nsICmdLineService to use wstring and ensure
michael@0 659 // all the Unicode data come in is correctly converted.
michael@0 660 // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases
michael@0 661 // what we have is actually a "utf8" version of a "utf16" string that's
michael@0 662 // actually byte-expanded native-encoding data. Someone upstream needs
michael@0 663 // to stop using AssignWithConversion and do things correctly. See bug
michael@0 664 // 58866 for what happens if we remove this
michael@0 665 // PossiblyByteExpandedFileName check.
michael@0 666 NS_ConvertUTF8toUTF16 in(aIn);
michael@0 667 if (PossiblyByteExpandedFileName(in)) {
michael@0 668 // removes high byte
michael@0 669 rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false, getter_AddRefs(filePath));
michael@0 670 }
michael@0 671 else {
michael@0 672 // input is unicode
michael@0 673 rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath));
michael@0 674 }
michael@0 675
michael@0 676 if (NS_SUCCEEDED(rv))
michael@0 677 {
michael@0 678 NS_GetURLSpecFromFile(filePath, aOut);
michael@0 679 return NS_OK;
michael@0 680 }
michael@0 681 }
michael@0 682
michael@0 683 return NS_ERROR_FAILURE;
michael@0 684 }
michael@0 685
michael@0 686 bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl)
michael@0 687 {
michael@0 688 // Oh dear, the protocol is invalid. Test if the protocol might
michael@0 689 // actually be a url without a protocol:
michael@0 690 //
michael@0 691 // http://www.faqs.org/rfcs/rfc1738.html
michael@0 692 // http://www.faqs.org/rfcs/rfc2396.html
michael@0 693 //
michael@0 694 // e.g. Anything of the form:
michael@0 695 //
michael@0 696 // <hostname>:<port> or
michael@0 697 // <hostname>:<port>/
michael@0 698 //
michael@0 699 // Where <hostname> is a string of alphanumeric characters and dashes
michael@0 700 // separated by dots.
michael@0 701 // and <port> is a 5 or less digits. This actually breaks the rfc2396
michael@0 702 // definition of a scheme which allows dots in schemes.
michael@0 703 //
michael@0 704 // Note:
michael@0 705 // People expecting this to work with
michael@0 706 // <user>:<password>@<host>:<port>/<url-path> will be disappointed!
michael@0 707 //
michael@0 708 // Note: Parser could be a lot tighter, tossing out silly hostnames
michael@0 709 // such as those containing consecutive dots and so on.
michael@0 710
michael@0 711 // Read the hostname which should of the form
michael@0 712 // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
michael@0 713
michael@0 714 nsACString::const_iterator iterBegin;
michael@0 715 nsACString::const_iterator iterEnd;
michael@0 716 aUrl.BeginReading(iterBegin);
michael@0 717 aUrl.EndReading(iterEnd);
michael@0 718 nsACString::const_iterator iter = iterBegin;
michael@0 719
michael@0 720 while (iter != iterEnd)
michael@0 721 {
michael@0 722 uint32_t chunkSize = 0;
michael@0 723 // Parse a chunk of the address
michael@0 724 while (iter != iterEnd &&
michael@0 725 (*iter == '-' ||
michael@0 726 nsCRT::IsAsciiAlpha(*iter) ||
michael@0 727 nsCRT::IsAsciiDigit(*iter)))
michael@0 728 {
michael@0 729 ++chunkSize;
michael@0 730 ++iter;
michael@0 731 }
michael@0 732 if (chunkSize == 0 || iter == iterEnd)
michael@0 733 {
michael@0 734 return false;
michael@0 735 }
michael@0 736 if (*iter == ':')
michael@0 737 {
michael@0 738 // Go onto checking the for the digits
michael@0 739 break;
michael@0 740 }
michael@0 741 if (*iter != '.')
michael@0 742 {
michael@0 743 // Whatever it is, it ain't a hostname!
michael@0 744 return false;
michael@0 745 }
michael@0 746 ++iter;
michael@0 747 }
michael@0 748 if (iter == iterEnd)
michael@0 749 {
michael@0 750 // No point continuing since there is no colon
michael@0 751 return false;
michael@0 752 }
michael@0 753 ++iter;
michael@0 754
michael@0 755 // Count the number of digits after the colon and before the
michael@0 756 // next forward slash (or end of string)
michael@0 757
michael@0 758 uint32_t digitCount = 0;
michael@0 759 while (iter != iterEnd && digitCount <= 5)
michael@0 760 {
michael@0 761 if (nsCRT::IsAsciiDigit(*iter))
michael@0 762 {
michael@0 763 digitCount++;
michael@0 764 }
michael@0 765 else if (*iter == '/')
michael@0 766 {
michael@0 767 break;
michael@0 768 }
michael@0 769 else
michael@0 770 {
michael@0 771 // Whatever it is, it ain't a port!
michael@0 772 return false;
michael@0 773 }
michael@0 774 ++iter;
michael@0 775 }
michael@0 776 if (digitCount == 0 || digitCount > 5)
michael@0 777 {
michael@0 778 // No digits or more digits than a port would have.
michael@0 779 return false;
michael@0 780 }
michael@0 781
michael@0 782 // Yes, it's possibly a host:port url
michael@0 783 return true;
michael@0 784 }
michael@0 785
michael@0 786 bool nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn)
michael@0 787 {
michael@0 788 // XXXXX HACK XXXXX : please don't copy this code.
michael@0 789 // There are cases where aIn contains the locale byte chars padded to short
michael@0 790 // (thus the name "ByteExpanded"); whereas other cases
michael@0 791 // have proper Unicode code points.
michael@0 792 // This is a temporary fix. Please refer to 58866, 86948
michael@0 793
michael@0 794 nsReadingIterator<char16_t> iter;
michael@0 795 nsReadingIterator<char16_t> iterEnd;
michael@0 796 aIn.BeginReading(iter);
michael@0 797 aIn.EndReading(iterEnd);
michael@0 798 while (iter != iterEnd)
michael@0 799 {
michael@0 800 if (*iter >= 0x0080 && *iter <= 0x00FF)
michael@0 801 return true;
michael@0 802 ++iter;
michael@0 803 }
michael@0 804 return false;
michael@0 805 }
michael@0 806
michael@0 807 void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
michael@0 808 nsIInputStream **aPostData,
michael@0 809 nsIURI** aURI)
michael@0 810 {
michael@0 811 // These are keyword formatted strings
michael@0 812 // "what is mozilla"
michael@0 813 // "what is mozilla?"
michael@0 814 // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
michael@0 815 // "?mozilla" - anything that begins with a question mark
michael@0 816 // "?site:mozilla.org docshell"
michael@0 817 // Things that have a quote before the first dot/colon
michael@0 818
michael@0 819 // These are not keyword formatted strings
michael@0 820 // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
michael@0 821 // "www.blah.com stuff"
michael@0 822 // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
michael@0 823 // "nonQualifiedHost:80 args"
michael@0 824 // "nonQualifiedHost?"
michael@0 825 // "nonQualifiedHost?args"
michael@0 826 // "nonQualifiedHost?some args"
michael@0 827
michael@0 828 // Note: uint32_t(kNotFound) is greater than any actual location
michael@0 829 // in practice. So if we cast all locations to uint32_t, then a <
michael@0 830 // b guarantees that either b is kNotFound and a is found, or both
michael@0 831 // are found and a found before b.
michael@0 832 uint32_t dotLoc = uint32_t(aURIString.FindChar('.'));
michael@0 833 uint32_t colonLoc = uint32_t(aURIString.FindChar(':'));
michael@0 834 uint32_t spaceLoc = uint32_t(aURIString.FindChar(' '));
michael@0 835 if (spaceLoc == 0) {
michael@0 836 // Treat this as not found
michael@0 837 spaceLoc = uint32_t(kNotFound);
michael@0 838 }
michael@0 839 uint32_t qMarkLoc = uint32_t(aURIString.FindChar('?'));
michael@0 840 uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')),
michael@0 841 uint32_t(aURIString.FindChar('\'')));
michael@0 842
michael@0 843 if (((spaceLoc < dotLoc || quoteLoc < dotLoc) &&
michael@0 844 (spaceLoc < colonLoc || quoteLoc < colonLoc) &&
michael@0 845 (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) ||
michael@0 846 qMarkLoc == 0)
michael@0 847 {
michael@0 848 KeywordToURI(aURIString, aPostData, aURI);
michael@0 849 }
michael@0 850 }
michael@0 851
michael@0 852
michael@0 853 nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup)
michael@0 854 {
michael@0 855 nsDefaultURIFixup *fixup = new nsDefaultURIFixup;
michael@0 856 if (fixup == nullptr)
michael@0 857 {
michael@0 858 return NS_ERROR_OUT_OF_MEMORY;
michael@0 859 }
michael@0 860 return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup);
michael@0 861 }
michael@0 862

mercurial