michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsCRT.h" michael@0: michael@0: #include "nsIFile.h" michael@0: #include michael@0: michael@0: #ifdef MOZ_TOOLKIT_SEARCH michael@0: #include "nsIBrowserSearchService.h" michael@0: #endif michael@0: michael@0: #include "nsIURIFixup.h" michael@0: #include "nsDefaultURIFixup.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /* Implementation file */ michael@0: NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup) michael@0: michael@0: static bool sInitializedPrefCaches = false; michael@0: static bool sFixTypos = true; michael@0: michael@0: nsDefaultURIFixup::nsDefaultURIFixup() michael@0: { michael@0: /* member initializers and constructor code */ michael@0: } michael@0: michael@0: michael@0: nsDefaultURIFixup::~nsDefaultURIFixup() michael@0: { michael@0: /* destructor code */ michael@0: } michael@0: michael@0: /* nsIURI createExposableURI (in nsIURI aURI); */ michael@0: NS_IMETHODIMP michael@0: nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: NS_ENSURE_ARG_POINTER(aReturn); michael@0: michael@0: bool isWyciwyg = false; michael@0: aURI->SchemeIs("wyciwyg", &isWyciwyg); michael@0: michael@0: nsAutoCString userPass; michael@0: aURI->GetUserPass(userPass); michael@0: michael@0: // most of the time we can just AddRef and return michael@0: if (!isWyciwyg && userPass.IsEmpty()) michael@0: { michael@0: *aReturn = aURI; michael@0: NS_ADDREF(*aReturn); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Rats, we have to massage the URI michael@0: nsCOMPtr uri; michael@0: if (isWyciwyg) michael@0: { michael@0: nsAutoCString path; michael@0: nsresult rv = aURI->GetPath(path); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t pathLength = path.Length(); michael@0: if (pathLength <= 2) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Path is of the form "//123/http://foo/bar", with a variable number of digits. michael@0: // To figure out where the "real" URL starts, search path for a '/', starting at michael@0: // the third character. michael@0: int32_t slashIndex = path.FindChar('/', 2); michael@0: if (slashIndex == kNotFound) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Get the charset of the original URI so we can pass it to our fixed up URI. michael@0: nsAutoCString charset; michael@0: aURI->GetOriginCharset(charset); michael@0: michael@0: rv = NS_NewURI(getter_AddRefs(uri), michael@0: Substring(path, slashIndex + 1, pathLength - slashIndex - 1), michael@0: charset.get()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else michael@0: { michael@0: // clone the URI so zapping user:pass doesn't change the original michael@0: nsresult rv = aURI->Clone(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // hide user:pass unless overridden by pref michael@0: if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) michael@0: { michael@0: uri->SetUserPass(EmptyCString()); michael@0: } michael@0: michael@0: // return the fixed-up URI michael@0: *aReturn = uri; michael@0: NS_ADDREF(*aReturn); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIURI createFixupURI (in nsAUTF8String aURIText, in unsigned long aFixupFlags); */ michael@0: NS_IMETHODIMP michael@0: nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags, michael@0: nsIInputStream **aPostData, nsIURI **aURI) michael@0: { michael@0: NS_ENSURE_ARG(!aStringURI.IsEmpty()); michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: michael@0: nsresult rv; michael@0: *aURI = nullptr; michael@0: michael@0: nsAutoCString uriString(aStringURI); michael@0: uriString.Trim(" "); // Cleanup the empty spaces that might be on each end. michael@0: michael@0: // Eliminate embedded newlines, which single-line text fields now allow: michael@0: uriString.StripChars("\r\n"); michael@0: michael@0: NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsAutoCString scheme; michael@0: ioService->ExtractScheme(aStringURI, scheme); michael@0: michael@0: // View-source is a pseudo scheme. We're interested in fixing up the stuff michael@0: // after it. The easiest way to do that is to call this method again with the michael@0: // "view-source:" lopped off and then prepend it again afterwards. michael@0: michael@0: if (scheme.LowerCaseEqualsLiteral("view-source")) michael@0: { michael@0: nsCOMPtr uri; michael@0: uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; michael@0: michael@0: rv = CreateFixupURI(Substring(uriString, michael@0: sizeof("view-source:") - 1, michael@0: uriString.Length() - michael@0: (sizeof("view-source:") - 1)), michael@0: newFixupFlags, aPostData, getter_AddRefs(uri)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: nsAutoCString spec; michael@0: uri->GetSpec(spec); michael@0: uriString.Assign(NS_LITERAL_CSTRING("view-source:") + spec); michael@0: } michael@0: else { michael@0: // Check for if it is a file URL michael@0: FileURIFixup(uriString, aURI); michael@0: if(*aURI) michael@0: return NS_OK; michael@0: michael@0: #if defined(XP_WIN) michael@0: // Not a file URL, so translate '\' to '/' for convenience in the common protocols michael@0: // e.g. catch michael@0: // michael@0: // http:\\broken.com\address michael@0: // http:\\broken.com/blah michael@0: // broken.com\blah michael@0: // michael@0: // Code will also do partial fix up the following urls michael@0: // michael@0: // http:\\broken.com\address/somewhere\image.jpg (stops at first forward slash) michael@0: // http:\\broken.com\blah?arg=somearg\foo.jpg (stops at question mark) michael@0: // http:\\broken.com#odd\ref (stops at hash) michael@0: // michael@0: if (scheme.IsEmpty() || michael@0: scheme.LowerCaseEqualsLiteral("http") || michael@0: scheme.LowerCaseEqualsLiteral("https") || michael@0: scheme.LowerCaseEqualsLiteral("ftp")) michael@0: { michael@0: // Walk the string replacing backslashes with forward slashes until michael@0: // the end is reached, or a question mark, or a hash, or a forward michael@0: // slash. The forward slash test is to stop before trampling over michael@0: // URIs which legitimately contain a mix of both forward and michael@0: // backward slashes. michael@0: nsAutoCString::iterator start; michael@0: nsAutoCString::iterator end; michael@0: uriString.BeginWriting(start); michael@0: uriString.EndWriting(end); michael@0: while (start != end) { michael@0: if (*start == '?' || *start == '#' || *start == '/') michael@0: break; michael@0: if (*start == '\\') michael@0: *start = '/'; michael@0: ++start; michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: if (!sInitializedPrefCaches) { michael@0: // Check if we want to fix up common scheme typos. michael@0: rv = Preferences::AddBoolVarCache(&sFixTypos, michael@0: "browser.fixup.typo.scheme", michael@0: sFixTypos); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), michael@0: "Failed to observe \"browser.fixup.typo.scheme\""); michael@0: sInitializedPrefCaches = true; michael@0: } michael@0: michael@0: // Fix up common scheme typos. michael@0: if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) { michael@0: michael@0: // Fast-path for common cases. michael@0: if (scheme.IsEmpty() || michael@0: scheme.LowerCaseEqualsLiteral("http") || michael@0: scheme.LowerCaseEqualsLiteral("https") || michael@0: scheme.LowerCaseEqualsLiteral("ftp") || michael@0: scheme.LowerCaseEqualsLiteral("file")) { michael@0: // Do nothing. michael@0: } else if (scheme.LowerCaseEqualsLiteral("ttp")) { michael@0: // ttp -> http. michael@0: uriString.Replace(0, 3, "http"); michael@0: scheme.AssignLiteral("http"); michael@0: } else if (scheme.LowerCaseEqualsLiteral("ttps")) { michael@0: // ttps -> https. michael@0: uriString.Replace(0, 4, "https"); michael@0: scheme.AssignLiteral("https"); michael@0: } else if (scheme.LowerCaseEqualsLiteral("tps")) { michael@0: // tps -> https. michael@0: uriString.Replace(0, 3, "https"); michael@0: scheme.AssignLiteral("https"); michael@0: } else if (scheme.LowerCaseEqualsLiteral("ps")) { michael@0: // ps -> https. michael@0: uriString.Replace(0, 2, "https"); michael@0: scheme.AssignLiteral("https"); michael@0: } else if (scheme.LowerCaseEqualsLiteral("ile")) { michael@0: // ile -> file. michael@0: uriString.Replace(0, 3, "file"); michael@0: scheme.AssignLiteral("file"); michael@0: } else if (scheme.LowerCaseEqualsLiteral("le")) { michael@0: // le -> file. michael@0: uriString.Replace(0, 2, "file"); michael@0: scheme.AssignLiteral("file"); michael@0: } michael@0: } michael@0: michael@0: // Now we need to check whether "scheme" is something we don't michael@0: // really know about. michael@0: nsCOMPtr ourHandler, extHandler; michael@0: michael@0: ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler)); michael@0: extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default"); michael@0: michael@0: if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) { michael@0: // Just try to create an URL out of it michael@0: rv = NS_NewURI(aURI, uriString, nullptr); michael@0: michael@0: if (!*aURI && rv != NS_ERROR_MALFORMED_URI) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: if (*aURI) { michael@0: if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) michael@0: MakeAlternateURI(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // See if it is a keyword michael@0: // Test whether keywords need to be fixed up michael@0: bool fixupKeywords = false; michael@0: if (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) { michael@0: nsresult rv = Preferences::GetBool("keyword.enabled", &fixupKeywords); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); michael@0: if (fixupKeywords) michael@0: { michael@0: KeywordURIFixup(uriString, aPostData, aURI); michael@0: if(*aURI) michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Prune duff protocol schemes michael@0: // michael@0: // ://totallybroken.url.com michael@0: // //shorthand.url.com michael@0: // michael@0: if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) michael@0: { michael@0: uriString = StringTail(uriString, uriString.Length() - 3); michael@0: } michael@0: else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) michael@0: { michael@0: uriString = StringTail(uriString, uriString.Length() - 2); michael@0: } michael@0: michael@0: // Add ftp:// or http:// to front of url if it has no spec michael@0: // michael@0: // Should fix: michael@0: // michael@0: // no-scheme.com michael@0: // ftp.no-scheme.com michael@0: // ftp4.no-scheme.com michael@0: // no-scheme.com/query?foo=http://www.foo.com michael@0: // michael@0: int32_t schemeDelim = uriString.Find("://",0); michael@0: int32_t firstDelim = uriString.FindCharInSet("/:"); michael@0: if (schemeDelim <= 0 || michael@0: (firstDelim != -1 && schemeDelim > firstDelim)) { michael@0: // find host name michael@0: int32_t hostPos = uriString.FindCharInSet("/:?#"); michael@0: if (hostPos == -1) michael@0: hostPos = uriString.Length(); michael@0: michael@0: // extract host name michael@0: nsAutoCString hostSpec; michael@0: uriString.Left(hostSpec, hostPos); michael@0: michael@0: // insert url spec corresponding to host name michael@0: if (IsLikelyFTP(hostSpec)) michael@0: uriString.Assign(NS_LITERAL_CSTRING("ftp://") + uriString); michael@0: else michael@0: uriString.Assign(NS_LITERAL_CSTRING("http://") + uriString); michael@0: } // end if checkprotocol michael@0: michael@0: rv = NS_NewURI(aURI, uriString, nullptr); michael@0: michael@0: // Did the caller want us to try an alternative URI? michael@0: // If so, attempt to fixup http://foo into http://www.foo.com michael@0: michael@0: if (*aURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) { michael@0: MakeAlternateURI(*aURI); michael@0: } michael@0: michael@0: // If we still haven't been able to construct a valid URI, try to force a michael@0: // keyword match. This catches search strings with '.' or ':' in them. michael@0: if (!*aURI && fixupKeywords) michael@0: { michael@0: KeywordToURI(aStringURI, aPostData, aURI); michael@0: if(*aURI) michael@0: return NS_OK; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword, michael@0: nsIInputStream **aPostData, michael@0: nsIURI **aURI) michael@0: { michael@0: *aURI = nullptr; michael@0: if (aPostData) { michael@0: *aPostData = nullptr; michael@0: } michael@0: NS_ENSURE_STATE(Preferences::GetRootBranch()); michael@0: michael@0: // Strip leading "?" and leading/trailing spaces from aKeyword michael@0: nsAutoCString keyword(aKeyword); michael@0: if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) { michael@0: keyword.Cut(0, 1); michael@0: } michael@0: keyword.Trim(" "); michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); michael@0: if (!contentChild) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: ipc::OptionalInputStreamParams postData; michael@0: ipc::OptionalURIParams uri; michael@0: if (!contentChild->SendKeywordToURI(keyword, &postData, &uri)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aPostData) { michael@0: nsTArray fds; michael@0: nsCOMPtr temp = DeserializeInputStream(postData, fds); michael@0: temp.forget(aPostData); michael@0: michael@0: MOZ_ASSERT(fds.IsEmpty()); michael@0: } michael@0: michael@0: nsCOMPtr temp = DeserializeURI(uri); michael@0: temp.forget(aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_TOOLKIT_SEARCH michael@0: // Try falling back to the search service's default search engine michael@0: nsCOMPtr searchSvc = do_GetService("@mozilla.org/browser/search-service;1"); michael@0: if (searchSvc) { michael@0: nsCOMPtr defaultEngine; michael@0: searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine)); michael@0: if (defaultEngine) { michael@0: nsCOMPtr submission; michael@0: // We allow default search plugins to specify alternate michael@0: // parameters that are specific to keyword searches. michael@0: NS_NAMED_LITERAL_STRING(mozKeywordSearch, "application/x-moz-keywordsearch"); michael@0: bool supportsResponseType = false; michael@0: defaultEngine->SupportsResponseType(mozKeywordSearch, &supportsResponseType); michael@0: if (supportsResponseType) michael@0: defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword), michael@0: mozKeywordSearch, michael@0: NS_LITERAL_STRING("keyword"), michael@0: getter_AddRefs(submission)); michael@0: else michael@0: defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword), michael@0: EmptyString(), michael@0: NS_LITERAL_STRING("keyword"), michael@0: getter_AddRefs(submission)); michael@0: if (submission) { michael@0: nsCOMPtr postData; michael@0: submission->GetPostData(getter_AddRefs(postData)); michael@0: if (aPostData) { michael@0: postData.forget(aPostData); michael@0: } else if (postData) { michael@0: // The submission specifies POST data (i.e. the search michael@0: // engine's "method" is POST), but our caller didn't allow michael@0: // passing post data back. No point passing back a URL that michael@0: // won't load properly. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // This notification is meant for Firefox Health Report so it michael@0: // can increment counts from the search engine. The assumption michael@0: // here is that this keyword/submission will eventually result michael@0: // in a search. Since we only generate a URI here, there is the michael@0: // possibility we'll increment the counter without actually michael@0: // incurring a search. A robust solution would involve currying michael@0: // the search engine's name through various function calls. michael@0: nsCOMPtr obsSvc = mozilla::services::GetObserverService(); michael@0: if (obsSvc) { michael@0: // Note that "keyword-search" refers to a search via the url michael@0: // bar, not a bookmarks keyword search. michael@0: obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get()); michael@0: } michael@0: michael@0: return submission->GetUri(aURI); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // out of options michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: bool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI) michael@0: { michael@0: if (!Preferences::GetRootBranch()) michael@0: { michael@0: return false; michael@0: } michael@0: if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // Code only works for http. Not for any other protocol including https! michael@0: bool isHttp = false; michael@0: aURI->SchemeIs("http", &isHttp); michael@0: if (!isHttp) { michael@0: return false; michael@0: } michael@0: michael@0: // Security - URLs with user / password info should NOT be fixed up michael@0: nsAutoCString userpass; michael@0: aURI->GetUserPass(userpass); michael@0: if (!userpass.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoCString oldHost; michael@0: nsAutoCString newHost; michael@0: aURI->GetHost(oldHost); michael@0: michael@0: // Count the dots michael@0: int32_t numDots = 0; michael@0: nsReadingIterator iter; michael@0: nsReadingIterator iterEnd; michael@0: oldHost.BeginReading(iter); michael@0: oldHost.EndReading(iterEnd); michael@0: while (iter != iterEnd) { michael@0: if (*iter == '.') michael@0: numDots++; michael@0: ++iter; michael@0: } michael@0: michael@0: michael@0: // Get the prefix and suffix to stick onto the new hostname. By default these michael@0: // are www. & .com but they could be any other value, e.g. www. & .org michael@0: michael@0: nsAutoCString prefix("www."); michael@0: nsAdoptingCString prefPrefix = michael@0: Preferences::GetCString("browser.fixup.alternate.prefix"); michael@0: if (prefPrefix) michael@0: { michael@0: prefix.Assign(prefPrefix); michael@0: } michael@0: michael@0: nsAutoCString suffix(".com"); michael@0: nsAdoptingCString prefSuffix = michael@0: Preferences::GetCString("browser.fixup.alternate.suffix"); michael@0: if (prefSuffix) michael@0: { michael@0: suffix.Assign(prefSuffix); michael@0: } michael@0: michael@0: if (numDots == 0) michael@0: { michael@0: newHost.Assign(prefix); michael@0: newHost.Append(oldHost); michael@0: newHost.Append(suffix); michael@0: } michael@0: else if (numDots == 1) michael@0: { michael@0: if (!prefix.IsEmpty() && michael@0: oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) { michael@0: newHost.Assign(oldHost); michael@0: newHost.Append(suffix); michael@0: } michael@0: else if (!suffix.IsEmpty()) { michael@0: newHost.Assign(prefix); michael@0: newHost.Append(oldHost); michael@0: } michael@0: else michael@0: { michael@0: // Do nothing michael@0: return false; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: // Do nothing michael@0: return false; michael@0: } michael@0: michael@0: if (newHost.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: // Assign the new host string over the old one michael@0: aURI->SetHost(newHost); michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Check if the host name starts with ftp\d*\. and it's not directly followed michael@0: * by the tld. michael@0: */ michael@0: bool nsDefaultURIFixup::IsLikelyFTP(const nsCString &aHostSpec) michael@0: { michael@0: bool likelyFTP = false; michael@0: if (aHostSpec.EqualsIgnoreCase("ftp", 3)) { michael@0: nsACString::const_iterator iter; michael@0: nsACString::const_iterator end; michael@0: aHostSpec.BeginReading(iter); michael@0: aHostSpec.EndReading(end); michael@0: iter.advance(3); // move past the "ftp" part michael@0: michael@0: while (iter != end) michael@0: { michael@0: if (*iter == '.') { michael@0: // now make sure the name has at least one more dot in it michael@0: ++iter; michael@0: while (iter != end) michael@0: { michael@0: if (*iter == '.') { michael@0: likelyFTP = true; michael@0: break; michael@0: } michael@0: ++iter; michael@0: } michael@0: break; michael@0: } michael@0: else if (!nsCRT::IsAsciiDigit(*iter)) { michael@0: break; michael@0: } michael@0: ++iter; michael@0: } michael@0: } michael@0: return likelyFTP; michael@0: } michael@0: michael@0: nsresult nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI, michael@0: nsIURI** aURI) michael@0: { michael@0: nsAutoCString uriSpecOut; michael@0: michael@0: nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut); michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: // if this is file url, uriSpecOut is already in FS charset michael@0: if(NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr))) michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn, michael@0: nsCString& aOut) michael@0: { michael@0: bool attemptFixup = false; michael@0: michael@0: #if defined(XP_WIN) michael@0: // Check for \ in the url-string or just a drive (PC) michael@0: if(kNotFound != aIn.FindChar('\\') || michael@0: (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) michael@0: { michael@0: attemptFixup = true; michael@0: } michael@0: #elif defined(XP_UNIX) michael@0: // Check if it starts with / (UNIX) michael@0: if(aIn.First() == '/') michael@0: { michael@0: attemptFixup = true; michael@0: } michael@0: #else michael@0: // Do nothing (All others for now) michael@0: #endif michael@0: michael@0: if (attemptFixup) michael@0: { michael@0: // Test if this is a valid path by trying to create a local file michael@0: // object. The URL of that is returned if successful. michael@0: michael@0: // NOTE: Please be sure to check that the call to NS_NewLocalFile michael@0: // rejects bad file paths when using this code on a new michael@0: // platform. michael@0: michael@0: nsCOMPtr filePath; michael@0: nsresult rv; michael@0: michael@0: // this is not the real fix but a temporary fix michael@0: // in order to really fix the problem, we need to change the michael@0: // nsICmdLineService interface to use wstring to pass paramenters michael@0: // instead of string since path name and other argument could be michael@0: // in non ascii.(see bug 87127) Since it is too risky to make interface change right michael@0: // now, we decide not to do so now. michael@0: // Therefore, the aIn we receive here maybe already in damage form michael@0: // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t michael@0: // while the real data could be in file system charset ) michael@0: // we choice the following logic which will work for most of the case. michael@0: // Case will still failed only if it meet ALL the following condiction: michael@0: // 1. running on CJK, Russian, or Greek system, and michael@0: // 2. user type it from URL bar michael@0: // 3. the file name contains character in the range of michael@0: // U+00A1-U+00FF but encode as different code point in file michael@0: // system charset (e.g. ACP on window)- this is very rare case michael@0: // We should remove this logic and convert to File system charset here michael@0: // once we change nsICmdLineService to use wstring and ensure michael@0: // all the Unicode data come in is correctly converted. michael@0: // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases michael@0: // what we have is actually a "utf8" version of a "utf16" string that's michael@0: // actually byte-expanded native-encoding data. Someone upstream needs michael@0: // to stop using AssignWithConversion and do things correctly. See bug michael@0: // 58866 for what happens if we remove this michael@0: // PossiblyByteExpandedFileName check. michael@0: NS_ConvertUTF8toUTF16 in(aIn); michael@0: if (PossiblyByteExpandedFileName(in)) { michael@0: // removes high byte michael@0: rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false, getter_AddRefs(filePath)); michael@0: } michael@0: else { michael@0: // input is unicode michael@0: rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath)); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: NS_GetURLSpecFromFile(filePath, aOut); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl) michael@0: { michael@0: // Oh dear, the protocol is invalid. Test if the protocol might michael@0: // actually be a url without a protocol: michael@0: // michael@0: // http://www.faqs.org/rfcs/rfc1738.html michael@0: // http://www.faqs.org/rfcs/rfc2396.html michael@0: // michael@0: // e.g. Anything of the form: michael@0: // michael@0: // : or michael@0: // :/ michael@0: // michael@0: // Where is a string of alphanumeric characters and dashes michael@0: // separated by dots. michael@0: // and is a 5 or less digits. This actually breaks the rfc2396 michael@0: // definition of a scheme which allows dots in schemes. michael@0: // michael@0: // Note: michael@0: // People expecting this to work with michael@0: // :@:/ will be disappointed! michael@0: // michael@0: // Note: Parser could be a lot tighter, tossing out silly hostnames michael@0: // such as those containing consecutive dots and so on. michael@0: michael@0: // Read the hostname which should of the form michael@0: // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*: michael@0: michael@0: nsACString::const_iterator iterBegin; michael@0: nsACString::const_iterator iterEnd; michael@0: aUrl.BeginReading(iterBegin); michael@0: aUrl.EndReading(iterEnd); michael@0: nsACString::const_iterator iter = iterBegin; michael@0: michael@0: while (iter != iterEnd) michael@0: { michael@0: uint32_t chunkSize = 0; michael@0: // Parse a chunk of the address michael@0: while (iter != iterEnd && michael@0: (*iter == '-' || michael@0: nsCRT::IsAsciiAlpha(*iter) || michael@0: nsCRT::IsAsciiDigit(*iter))) michael@0: { michael@0: ++chunkSize; michael@0: ++iter; michael@0: } michael@0: if (chunkSize == 0 || iter == iterEnd) michael@0: { michael@0: return false; michael@0: } michael@0: if (*iter == ':') michael@0: { michael@0: // Go onto checking the for the digits michael@0: break; michael@0: } michael@0: if (*iter != '.') michael@0: { michael@0: // Whatever it is, it ain't a hostname! michael@0: return false; michael@0: } michael@0: ++iter; michael@0: } michael@0: if (iter == iterEnd) michael@0: { michael@0: // No point continuing since there is no colon michael@0: return false; michael@0: } michael@0: ++iter; michael@0: michael@0: // Count the number of digits after the colon and before the michael@0: // next forward slash (or end of string) michael@0: michael@0: uint32_t digitCount = 0; michael@0: while (iter != iterEnd && digitCount <= 5) michael@0: { michael@0: if (nsCRT::IsAsciiDigit(*iter)) michael@0: { michael@0: digitCount++; michael@0: } michael@0: else if (*iter == '/') michael@0: { michael@0: break; michael@0: } michael@0: else michael@0: { michael@0: // Whatever it is, it ain't a port! michael@0: return false; michael@0: } michael@0: ++iter; michael@0: } michael@0: if (digitCount == 0 || digitCount > 5) michael@0: { michael@0: // No digits or more digits than a port would have. michael@0: return false; michael@0: } michael@0: michael@0: // Yes, it's possibly a host:port url michael@0: return true; michael@0: } michael@0: michael@0: bool nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn) michael@0: { michael@0: // XXXXX HACK XXXXX : please don't copy this code. michael@0: // There are cases where aIn contains the locale byte chars padded to short michael@0: // (thus the name "ByteExpanded"); whereas other cases michael@0: // have proper Unicode code points. michael@0: // This is a temporary fix. Please refer to 58866, 86948 michael@0: michael@0: nsReadingIterator iter; michael@0: nsReadingIterator iterEnd; michael@0: aIn.BeginReading(iter); michael@0: aIn.EndReading(iterEnd); michael@0: while (iter != iterEnd) michael@0: { michael@0: if (*iter >= 0x0080 && *iter <= 0x00FF) michael@0: return true; michael@0: ++iter; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString, michael@0: nsIInputStream **aPostData, michael@0: nsIURI** aURI) michael@0: { michael@0: // These are keyword formatted strings michael@0: // "what is mozilla" michael@0: // "what is mozilla?" michael@0: // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring michael@0: // "?mozilla" - anything that begins with a question mark michael@0: // "?site:mozilla.org docshell" michael@0: // Things that have a quote before the first dot/colon michael@0: michael@0: // These are not keyword formatted strings michael@0: // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?" michael@0: // "www.blah.com stuff" michael@0: // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?" michael@0: // "nonQualifiedHost:80 args" michael@0: // "nonQualifiedHost?" michael@0: // "nonQualifiedHost?args" michael@0: // "nonQualifiedHost?some args" michael@0: michael@0: // Note: uint32_t(kNotFound) is greater than any actual location michael@0: // in practice. So if we cast all locations to uint32_t, then a < michael@0: // b guarantees that either b is kNotFound and a is found, or both michael@0: // are found and a found before b. michael@0: uint32_t dotLoc = uint32_t(aURIString.FindChar('.')); michael@0: uint32_t colonLoc = uint32_t(aURIString.FindChar(':')); michael@0: uint32_t spaceLoc = uint32_t(aURIString.FindChar(' ')); michael@0: if (spaceLoc == 0) { michael@0: // Treat this as not found michael@0: spaceLoc = uint32_t(kNotFound); michael@0: } michael@0: uint32_t qMarkLoc = uint32_t(aURIString.FindChar('?')); michael@0: uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')), michael@0: uint32_t(aURIString.FindChar('\''))); michael@0: michael@0: if (((spaceLoc < dotLoc || quoteLoc < dotLoc) && michael@0: (spaceLoc < colonLoc || quoteLoc < colonLoc) && michael@0: (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) || michael@0: qMarkLoc == 0) michael@0: { michael@0: KeywordToURI(aURIString, aPostData, aURI); michael@0: } michael@0: } michael@0: michael@0: michael@0: nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup) michael@0: { michael@0: nsDefaultURIFixup *fixup = new nsDefaultURIFixup; michael@0: if (fixup == nullptr) michael@0: { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup); michael@0: } michael@0: