docshell/base/nsDefaultURIFixup.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial