docshell/base/nsDefaultURIFixup.cpp

changeset 0
6474c204b198
     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 +

mercurial