Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6 :
7 : #include "nsNetCID.h"
8 : #include "nsNetUtil.h"
9 : #include "nsIProtocolHandler.h"
10 : #include "nsCRT.h"
11 :
12 : #include "nsIFile.h"
13 : #include <algorithm>
14 :
15 : #ifdef MOZ_TOOLKIT_SEARCH
16 : #include "nsIBrowserSearchService.h"
17 : #endif
18 :
19 : #include "nsIURIFixup.h"
20 : #include "nsDefaultURIFixup.h"
21 : #include "mozilla/Preferences.h"
22 : #include "mozilla/dom/ContentChild.h"
23 : #include "mozilla/ipc/IPCStreamUtils.h"
24 : #include "mozilla/ipc/URIUtils.h"
25 : #include "mozilla/Tokenizer.h"
26 : #include "nsIObserverService.h"
27 : #include "nsXULAppAPI.h"
28 :
29 : // Used to check if external protocol schemes are usable
30 : #include "nsCExternalHandlerService.h"
31 : #include "nsIExternalProtocolService.h"
32 :
33 : using namespace mozilla;
34 :
35 : /* Implementation file */
36 244 : NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
37 :
38 : static bool sInitializedPrefCaches = false;
39 : static bool sFixTypos = true;
40 : static bool sDNSFirstForSingleWords = false;
41 : static bool sFixupKeywords = true;
42 :
43 2 : nsDefaultURIFixup::nsDefaultURIFixup()
44 : {
45 2 : }
46 :
47 0 : nsDefaultURIFixup::~nsDefaultURIFixup()
48 : {
49 0 : }
50 :
51 : NS_IMETHODIMP
52 28 : nsDefaultURIFixup::CreateExposableURI(nsIURI* aURI, nsIURI** aReturn)
53 : {
54 28 : NS_ENSURE_ARG_POINTER(aURI);
55 28 : NS_ENSURE_ARG_POINTER(aReturn);
56 :
57 28 : bool isWyciwyg = false;
58 28 : aURI->SchemeIs("wyciwyg", &isWyciwyg);
59 :
60 56 : nsAutoCString userPass;
61 28 : aURI->GetUserPass(userPass);
62 :
63 : // most of the time we can just AddRef and return
64 28 : if (!isWyciwyg && userPass.IsEmpty()) {
65 28 : *aReturn = aURI;
66 28 : NS_ADDREF(*aReturn);
67 28 : return NS_OK;
68 : }
69 :
70 : // Rats, we have to massage the URI
71 0 : nsCOMPtr<nsIURI> uri;
72 0 : if (isWyciwyg) {
73 0 : nsAutoCString path;
74 0 : nsresult rv = aURI->GetPath(path);
75 0 : NS_ENSURE_SUCCESS(rv, rv);
76 :
77 0 : uint32_t pathLength = path.Length();
78 0 : if (pathLength <= 2) {
79 0 : return NS_ERROR_FAILURE;
80 : }
81 :
82 : // Path is of the form "//123/http://foo/bar", with a variable number of
83 : // digits. To figure out where the "real" URL starts, search path for a '/',
84 : // starting at the third character.
85 0 : int32_t slashIndex = path.FindChar('/', 2);
86 0 : if (slashIndex == kNotFound) {
87 0 : return NS_ERROR_FAILURE;
88 : }
89 :
90 : // Get the charset of the original URI so we can pass it to our fixed up
91 : // URI.
92 0 : nsAutoCString charset;
93 0 : aURI->GetOriginCharset(charset);
94 :
95 0 : rv = NS_NewURI(getter_AddRefs(uri),
96 0 : Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
97 0 : charset.get());
98 0 : NS_ENSURE_SUCCESS(rv, rv);
99 : } else {
100 : // clone the URI so zapping user:pass doesn't change the original
101 0 : nsresult rv = aURI->Clone(getter_AddRefs(uri));
102 0 : NS_ENSURE_SUCCESS(rv, rv);
103 : }
104 :
105 : // hide user:pass unless overridden by pref
106 0 : if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) {
107 0 : uri->SetUserPass(EmptyCString());
108 : }
109 :
110 0 : uri.forget(aReturn);
111 0 : return NS_OK;
112 : }
113 :
114 : NS_IMETHODIMP
115 4 : nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI,
116 : uint32_t aFixupFlags,
117 : nsIInputStream** aPostData, nsIURI** aURI)
118 : {
119 8 : nsCOMPtr<nsIURIFixupInfo> fixupInfo;
120 4 : nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
121 8 : getter_AddRefs(fixupInfo));
122 4 : NS_ENSURE_SUCCESS(rv, rv);
123 :
124 4 : fixupInfo->GetPreferredURI(aURI);
125 4 : return rv;
126 : }
127 :
128 : // Returns true if the URL contains a user:password@ or user@
129 : static bool
130 0 : HasUserPassword(const nsACString& aStringURI)
131 : {
132 0 : mozilla::Tokenizer parser(aStringURI);
133 0 : mozilla::Tokenizer::Token token;
134 :
135 : // May start with any of "protocol:", "protocol://", "//", "://"
136 0 : if (parser.Check(Tokenizer::TOKEN_WORD, token)) { // Skip protocol if any
137 : }
138 0 : if (parser.CheckChar(':')) { // Skip colon if found
139 : }
140 0 : while (parser.CheckChar('/')) { // Skip all of the following slashes
141 : }
142 :
143 0 : while (parser.Next(token)) {
144 0 : if (token.Type() == Tokenizer::TOKEN_CHAR) {
145 0 : if (token.AsChar() == '/') {
146 0 : return false;
147 : }
148 0 : if (token.AsChar() == '@') {
149 0 : return true;
150 : }
151 : }
152 : }
153 :
154 0 : return false;
155 : }
156 :
157 : NS_IMETHODIMP
158 9 : nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI,
159 : uint32_t aFixupFlags,
160 : nsIInputStream** aPostData,
161 : nsIURIFixupInfo** aInfo)
162 : {
163 9 : NS_ENSURE_ARG(!aStringURI.IsEmpty());
164 :
165 : nsresult rv;
166 :
167 18 : nsAutoCString uriString(aStringURI);
168 :
169 : // Eliminate embedded newlines, which single-line text fields now allow:
170 9 : uriString.StripCRLF();
171 : // Cleanup the empty spaces that might be on each end:
172 9 : uriString.Trim(" ");
173 :
174 9 : NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
175 :
176 18 : RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
177 9 : NS_ADDREF(*aInfo = info);
178 :
179 : nsCOMPtr<nsIIOService> ioService =
180 18 : do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
181 9 : NS_ENSURE_SUCCESS(rv, rv);
182 18 : nsAutoCString scheme;
183 9 : ioService->ExtractScheme(aStringURI, scheme);
184 :
185 : // View-source is a pseudo scheme. We're interested in fixing up the stuff
186 : // after it. The easiest way to do that is to call this method again with the
187 : // "view-source:" lopped off and then prepend it again afterwards.
188 :
189 9 : if (scheme.LowerCaseEqualsLiteral("view-source")) {
190 0 : nsCOMPtr<nsIURIFixupInfo> uriInfo;
191 : // We disable keyword lookup and alternate URIs so that small typos don't
192 : // cause us to look at very different domains
193 : uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
194 0 : & ~FIXUP_FLAGS_MAKE_ALTERNATE_URI;
195 :
196 0 : const uint32_t viewSourceLen = sizeof("view-source:") - 1;
197 0 : nsAutoCString innerURIString(Substring(uriString, viewSourceLen,
198 0 : uriString.Length() -
199 0 : viewSourceLen));
200 : // Prevent recursion:
201 0 : innerURIString.Trim(" ");
202 0 : nsAutoCString innerScheme;
203 0 : ioService->ExtractScheme(innerURIString, innerScheme);
204 0 : if (innerScheme.LowerCaseEqualsLiteral("view-source")) {
205 0 : return NS_ERROR_FAILURE;
206 : }
207 :
208 0 : rv = GetFixupURIInfo(innerURIString, newFixupFlags, aPostData,
209 0 : getter_AddRefs(uriInfo));
210 0 : if (NS_FAILED(rv)) {
211 0 : return NS_ERROR_FAILURE;
212 : }
213 0 : nsAutoCString spec;
214 0 : nsCOMPtr<nsIURI> uri;
215 0 : uriInfo->GetPreferredURI(getter_AddRefs(uri));
216 0 : if (!uri) {
217 0 : return NS_ERROR_FAILURE;
218 : }
219 0 : uri->GetSpec(spec);
220 0 : uriString.AssignLiteral("view-source:");
221 0 : uriString.Append(spec);
222 : } else {
223 : // Check for if it is a file URL
224 18 : nsCOMPtr<nsIURI> uri;
225 9 : FileURIFixup(uriString, getter_AddRefs(uri));
226 : // NB: FileURIFixup only returns a URI if it had to fix the protocol to
227 : // do so, so passing in file:///foo/bar will not hit this path:
228 9 : if (uri) {
229 0 : uri.swap(info->mFixedURI);
230 0 : info->mPreferredURI = info->mFixedURI;
231 0 : info->mFixupChangedProtocol = true;
232 0 : return NS_OK;
233 : }
234 : }
235 :
236 9 : if (!sInitializedPrefCaches) {
237 : // Check if we want to fix up common scheme typos.
238 2 : rv = Preferences::AddBoolVarCache(&sFixTypos,
239 : "browser.fixup.typo.scheme",
240 : sFixTypos);
241 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
242 : "Failed to observe \"browser.fixup.typo.scheme\"");
243 :
244 2 : rv = Preferences::AddBoolVarCache(&sDNSFirstForSingleWords,
245 : "browser.fixup.dns_first_for_single_words",
246 : sDNSFirstForSingleWords);
247 2 : MOZ_ASSERT(NS_SUCCEEDED(rv),
248 : "Failed to observe \"browser.fixup.dns_first_for_single_words\"");
249 :
250 2 : rv = Preferences::AddBoolVarCache(&sFixupKeywords, "keyword.enabled",
251 : sFixupKeywords);
252 2 : MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to observe \"keyword.enabled\"");
253 2 : sInitializedPrefCaches = true;
254 : }
255 :
256 : // Fix up common scheme typos.
257 9 : if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
258 : // Fast-path for common cases.
259 12 : if (scheme.IsEmpty() ||
260 7 : scheme.LowerCaseEqualsLiteral("http") ||
261 6 : scheme.LowerCaseEqualsLiteral("https") ||
262 10 : scheme.LowerCaseEqualsLiteral("ftp") ||
263 3 : scheme.LowerCaseEqualsLiteral("file")) {
264 : // Do nothing.
265 3 : } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
266 : // ttp -> http.
267 0 : uriString.Replace(0, 3, "http");
268 0 : scheme.AssignLiteral("http");
269 0 : info->mFixupChangedProtocol = true;
270 3 : } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
271 : // ttps -> https.
272 0 : uriString.Replace(0, 4, "https");
273 0 : scheme.AssignLiteral("https");
274 0 : info->mFixupChangedProtocol = true;
275 3 : } else if (scheme.LowerCaseEqualsLiteral("tps")) {
276 : // tps -> https.
277 0 : uriString.Replace(0, 3, "https");
278 0 : scheme.AssignLiteral("https");
279 0 : info->mFixupChangedProtocol = true;
280 3 : } else if (scheme.LowerCaseEqualsLiteral("ps")) {
281 : // ps -> https.
282 0 : uriString.Replace(0, 2, "https");
283 0 : scheme.AssignLiteral("https");
284 0 : info->mFixupChangedProtocol = true;
285 3 : } else if (scheme.LowerCaseEqualsLiteral("ile")) {
286 : // ile -> file.
287 0 : uriString.Replace(0, 3, "file");
288 0 : scheme.AssignLiteral("file");
289 0 : info->mFixupChangedProtocol = true;
290 3 : } else if (scheme.LowerCaseEqualsLiteral("le")) {
291 : // le -> file.
292 0 : uriString.Replace(0, 2, "file");
293 0 : scheme.AssignLiteral("file");
294 0 : info->mFixupChangedProtocol = true;
295 : }
296 : }
297 :
298 : // Now we need to check whether "scheme" is something we don't
299 : // really know about.
300 18 : nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
301 :
302 9 : ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
303 9 : extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default");
304 :
305 9 : if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
306 : // Just try to create an URL out of it
307 6 : rv = NS_NewURI(getter_AddRefs(info->mFixedURI), uriString, nullptr);
308 :
309 6 : if (!info->mFixedURI && rv != NS_ERROR_MALFORMED_URI) {
310 0 : return rv;
311 : }
312 : }
313 :
314 9 : if (info->mFixedURI && ourHandler == extHandler && sFixupKeywords &&
315 0 : (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
316 : nsCOMPtr<nsIExternalProtocolService> extProtService =
317 0 : do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
318 0 : if (extProtService) {
319 0 : bool handlerExists = false;
320 0 : rv = extProtService->ExternalProtocolHandlerExists(scheme.get(),
321 0 : &handlerExists);
322 0 : if (NS_FAILED(rv)) {
323 0 : return rv;
324 : }
325 : // This basically means we're dealing with a theoretically valid
326 : // URI... but we have no idea how to load it. (e.g. "christmas:humbug")
327 : // It's more likely the user wants to search, and so we
328 : // chuck this over to their preferred search provider instead:
329 0 : if (!handlerExists) {
330 0 : bool hasUserPassword = HasUserPassword(uriString);
331 0 : if (!hasUserPassword) {
332 0 : TryKeywordFixupForURIInfo(uriString, info, aPostData);
333 : } else {
334 : // If the given URL has a user:password we can't just pass it to the
335 : // external protocol handler; we'll try using it with http instead later
336 0 : info->mFixedURI = nullptr;
337 : }
338 : }
339 : }
340 : }
341 :
342 9 : if (info->mFixedURI) {
343 6 : if (!info->mPreferredURI) {
344 6 : if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
345 0 : info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
346 : }
347 6 : info->mPreferredURI = info->mFixedURI;
348 : }
349 6 : return NS_OK;
350 : }
351 :
352 : // Fix up protocol string before calling KeywordURIFixup, because
353 : // it cares about the hostname of such URIs:
354 6 : nsCOMPtr<nsIURI> uriWithProtocol;
355 3 : bool inputHadDuffProtocol = false;
356 :
357 : // Prune duff protocol schemes
358 : //
359 : // ://totallybroken.url.com
360 : // //shorthand.url.com
361 : //
362 3 : if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) {
363 0 : uriString = StringTail(uriString, uriString.Length() - 3);
364 0 : inputHadDuffProtocol = true;
365 3 : } else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) {
366 0 : uriString = StringTail(uriString, uriString.Length() - 2);
367 0 : inputHadDuffProtocol = true;
368 : }
369 :
370 : // NB: this rv gets returned at the end of this method if we never
371 : // do a keyword fixup after this (because the pref or the flags passed
372 : // might not let us).
373 3 : rv = FixupURIProtocol(uriString, info, getter_AddRefs(uriWithProtocol));
374 3 : if (uriWithProtocol) {
375 3 : info->mFixedURI = uriWithProtocol;
376 : }
377 :
378 : // See if it is a keyword
379 : // Test whether keywords need to be fixed up
380 6 : if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) &&
381 3 : !inputHadDuffProtocol) {
382 6 : if (NS_SUCCEEDED(KeywordURIFixup(uriString, info, aPostData)) &&
383 3 : info->mPreferredURI) {
384 0 : return NS_OK;
385 : }
386 : }
387 :
388 : // Did the caller want us to try an alternative URI?
389 : // If so, attempt to fixup http://foo into http://www.foo.com
390 :
391 3 : if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
392 0 : info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
393 : }
394 :
395 3 : if (info->mFixedURI) {
396 3 : info->mPreferredURI = info->mFixedURI;
397 3 : return NS_OK;
398 : }
399 :
400 : // If we still haven't been able to construct a valid URI, try to force a
401 : // keyword match. This catches search strings with '.' or ':' in them.
402 0 : if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
403 0 : rv = TryKeywordFixupForURIInfo(aStringURI, info, aPostData);
404 : }
405 :
406 0 : return rv;
407 : }
408 :
409 : NS_IMETHODIMP
410 0 : nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
411 : nsIInputStream** aPostData,
412 : nsIURIFixupInfo** aInfo)
413 : {
414 0 : RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aKeyword);
415 0 : NS_ADDREF(*aInfo = info);
416 :
417 0 : if (aPostData) {
418 0 : *aPostData = nullptr;
419 : }
420 0 : NS_ENSURE_STATE(Preferences::GetRootBranch());
421 :
422 : // Strip leading "?" and leading/trailing spaces from aKeyword
423 0 : nsAutoCString keyword(aKeyword);
424 0 : if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
425 0 : keyword.Cut(0, 1);
426 : }
427 0 : keyword.Trim(" ");
428 :
429 0 : if (XRE_IsContentProcess()) {
430 0 : dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
431 0 : if (!contentChild) {
432 0 : return NS_ERROR_NOT_AVAILABLE;
433 : }
434 :
435 0 : ipc::OptionalIPCStream postData;
436 0 : ipc::OptionalURIParams uri;
437 0 : nsAutoString providerName;
438 0 : if (!contentChild->SendKeywordToURI(keyword, &providerName, &postData,
439 : &uri)) {
440 0 : return NS_ERROR_FAILURE;
441 : }
442 :
443 0 : CopyUTF8toUTF16(keyword, info->mKeywordAsSent);
444 0 : info->mKeywordProviderName = providerName;
445 :
446 0 : if (aPostData) {
447 0 : nsCOMPtr<nsIInputStream> temp = ipc::DeserializeIPCStream(postData);
448 0 : temp.forget(aPostData);
449 : }
450 :
451 0 : nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
452 0 : info->mPreferredURI = temp.forget();
453 0 : return NS_OK;
454 : }
455 :
456 : #ifdef MOZ_TOOLKIT_SEARCH
457 : // Try falling back to the search service's default search engine
458 : nsCOMPtr<nsIBrowserSearchService> searchSvc =
459 0 : do_GetService("@mozilla.org/browser/search-service;1");
460 0 : if (searchSvc) {
461 0 : nsCOMPtr<nsISearchEngine> defaultEngine;
462 0 : searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
463 0 : if (defaultEngine) {
464 0 : nsCOMPtr<nsISearchSubmission> submission;
465 0 : nsAutoString responseType;
466 : // We allow default search plugins to specify alternate
467 : // parameters that are specific to keyword searches.
468 0 : NS_NAMED_LITERAL_STRING(mozKeywordSearch,
469 : "application/x-moz-keywordsearch");
470 0 : bool supportsResponseType = false;
471 0 : defaultEngine->SupportsResponseType(mozKeywordSearch,
472 0 : &supportsResponseType);
473 0 : if (supportsResponseType) {
474 0 : responseType.Assign(mozKeywordSearch);
475 : }
476 :
477 0 : NS_ConvertUTF8toUTF16 keywordW(keyword);
478 0 : defaultEngine->GetSubmission(keywordW,
479 : responseType,
480 0 : NS_LITERAL_STRING("keyword"),
481 0 : getter_AddRefs(submission));
482 :
483 0 : if (submission) {
484 0 : nsCOMPtr<nsIInputStream> postData;
485 0 : submission->GetPostData(getter_AddRefs(postData));
486 0 : if (aPostData) {
487 0 : postData.forget(aPostData);
488 0 : } else if (postData) {
489 : // The submission specifies POST data (i.e. the search
490 : // engine's "method" is POST), but our caller didn't allow
491 : // passing post data back. No point passing back a URL that
492 : // won't load properly.
493 0 : return NS_ERROR_FAILURE;
494 : }
495 :
496 0 : defaultEngine->GetName(info->mKeywordProviderName);
497 0 : info->mKeywordAsSent = keywordW;
498 0 : return submission->GetUri(getter_AddRefs(info->mPreferredURI));
499 : }
500 : }
501 : }
502 : #endif
503 :
504 : // out of options
505 0 : return NS_ERROR_NOT_AVAILABLE;
506 : }
507 :
508 : // Helper to deal with passing around uri fixup stuff
509 : nsresult
510 0 : nsDefaultURIFixup::TryKeywordFixupForURIInfo(const nsACString& aURIString,
511 : nsDefaultURIFixupInfo* aFixupInfo,
512 : nsIInputStream** aPostData)
513 : {
514 0 : nsCOMPtr<nsIURIFixupInfo> keywordInfo;
515 0 : nsresult rv = KeywordToURI(aURIString, aPostData,
516 0 : getter_AddRefs(keywordInfo));
517 0 : if (NS_SUCCEEDED(rv)) {
518 0 : keywordInfo->GetKeywordProviderName(aFixupInfo->mKeywordProviderName);
519 0 : keywordInfo->GetKeywordAsSent(aFixupInfo->mKeywordAsSent);
520 0 : keywordInfo->GetPreferredURI(getter_AddRefs(aFixupInfo->mPreferredURI));
521 : }
522 0 : return rv;
523 : }
524 :
525 : bool
526 0 : nsDefaultURIFixup::MakeAlternateURI(nsIURI* aURI)
527 : {
528 0 : if (!Preferences::GetRootBranch()) {
529 0 : return false;
530 : }
531 0 : if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) {
532 0 : return false;
533 : }
534 :
535 : // Code only works for http. Not for any other protocol including https!
536 0 : bool isHttp = false;
537 0 : aURI->SchemeIs("http", &isHttp);
538 0 : if (!isHttp) {
539 0 : return false;
540 : }
541 :
542 : // Security - URLs with user / password info should NOT be fixed up
543 0 : nsAutoCString userpass;
544 0 : aURI->GetUserPass(userpass);
545 0 : if (!userpass.IsEmpty()) {
546 0 : return false;
547 : }
548 : // Don't fix up hosts with ports
549 : int32_t port;
550 0 : aURI->GetPort(&port);
551 0 : if (port != -1) {
552 0 : return false;
553 : }
554 :
555 0 : nsAutoCString oldHost;
556 0 : aURI->GetHost(oldHost);
557 :
558 : // Don't fix up 'localhost' because that's confusing:
559 0 : if (oldHost.EqualsLiteral("localhost")) {
560 0 : return false;
561 : }
562 :
563 0 : nsAutoCString newHost;
564 : // Count the dots
565 0 : int32_t numDots = 0;
566 0 : nsReadingIterator<char> iter;
567 0 : nsReadingIterator<char> iterEnd;
568 0 : oldHost.BeginReading(iter);
569 0 : oldHost.EndReading(iterEnd);
570 0 : while (iter != iterEnd) {
571 0 : if (*iter == '.') {
572 0 : numDots++;
573 : }
574 0 : ++iter;
575 : }
576 :
577 : // Get the prefix and suffix to stick onto the new hostname. By default these
578 : // are www. & .com but they could be any other value, e.g. www. & .org
579 :
580 0 : nsAutoCString prefix("www.");
581 : nsAdoptingCString prefPrefix =
582 0 : Preferences::GetCString("browser.fixup.alternate.prefix");
583 0 : if (prefPrefix) {
584 0 : prefix.Assign(prefPrefix);
585 : }
586 :
587 0 : nsAutoCString suffix(".com");
588 : nsAdoptingCString prefSuffix =
589 0 : Preferences::GetCString("browser.fixup.alternate.suffix");
590 0 : if (prefSuffix) {
591 0 : suffix.Assign(prefSuffix);
592 : }
593 :
594 0 : if (numDots == 0) {
595 0 : newHost.Assign(prefix);
596 0 : newHost.Append(oldHost);
597 0 : newHost.Append(suffix);
598 0 : } else if (numDots == 1) {
599 0 : if (!prefix.IsEmpty() &&
600 0 : oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
601 0 : newHost.Assign(oldHost);
602 0 : newHost.Append(suffix);
603 0 : } else if (!suffix.IsEmpty()) {
604 0 : newHost.Assign(prefix);
605 0 : newHost.Append(oldHost);
606 : } else {
607 : // Do nothing
608 0 : return false;
609 : }
610 : } else {
611 : // Do nothing
612 0 : return false;
613 : }
614 :
615 0 : if (newHost.IsEmpty()) {
616 0 : return false;
617 : }
618 :
619 : // Assign the new host string over the old one
620 0 : aURI->SetHost(newHost);
621 0 : return true;
622 : }
623 :
624 : nsresult
625 9 : nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI, nsIURI** aURI)
626 : {
627 18 : nsAutoCString uriSpecOut;
628 :
629 9 : nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
630 9 : if (NS_SUCCEEDED(rv)) {
631 : // if this is file url, uriSpecOut is already in FS charset
632 0 : if (NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr))) {
633 0 : return NS_OK;
634 : }
635 : }
636 9 : return NS_ERROR_FAILURE;
637 : }
638 :
639 : nsresult
640 9 : nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
641 : nsCString& aResult)
642 : {
643 9 : bool attemptFixup = false;
644 :
645 : #if defined(XP_WIN)
646 : // Check for \ in the url-string or just a drive (PC)
647 : if (aIn.Contains('\\') ||
648 : (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
649 : attemptFixup = true;
650 : }
651 : #elif defined(XP_UNIX)
652 : // Check if it starts with / (UNIX)
653 9 : if (aIn.First() == '/') {
654 0 : attemptFixup = true;
655 : }
656 : #else
657 : // Do nothing (All others for now)
658 : #endif
659 :
660 9 : if (attemptFixup) {
661 : // Test if this is a valid path by trying to create a local file
662 : // object. The URL of that is returned if successful.
663 :
664 0 : nsCOMPtr<nsIFile> filePath;
665 0 : nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aIn), false,
666 0 : getter_AddRefs(filePath));
667 :
668 0 : if (NS_SUCCEEDED(rv)) {
669 0 : NS_GetURLSpecFromFile(filePath, aResult);
670 0 : return NS_OK;
671 : }
672 : }
673 :
674 9 : return NS_ERROR_FAILURE;
675 : }
676 :
677 : nsresult
678 3 : nsDefaultURIFixup::FixupURIProtocol(const nsACString& aURIString,
679 : nsDefaultURIFixupInfo* aFixupInfo,
680 : nsIURI** aURI)
681 : {
682 6 : nsAutoCString uriString(aURIString);
683 3 : *aURI = nullptr;
684 :
685 : // Add ftp:// or http:// to front of url if it has no spec
686 : //
687 : // Should fix:
688 : //
689 : // no-scheme.com
690 : // ftp.no-scheme.com
691 : // ftp4.no-scheme.com
692 : // no-scheme.com/query?foo=http://www.foo.com
693 : // user:pass@no-scheme.com
694 : //
695 3 : int32_t schemeDelim = uriString.Find("://", 0);
696 3 : int32_t firstDelim = uriString.FindCharInSet("/:");
697 3 : if (schemeDelim <= 0 ||
698 0 : (firstDelim != -1 && schemeDelim > firstDelim)) {
699 : // find host name
700 3 : int32_t hostPos = uriString.FindCharInSet("/:?#");
701 3 : if (hostPos == -1) {
702 0 : hostPos = uriString.Length();
703 : }
704 :
705 : // extract host name
706 6 : nsAutoCString hostSpec;
707 3 : uriString.Left(hostSpec, hostPos);
708 :
709 : // insert url spec corresponding to host name
710 3 : uriString.InsertLiteral("http://", 0);
711 3 : aFixupInfo->mFixupChangedProtocol = true;
712 : } // end if checkprotocol
713 :
714 6 : return NS_NewURI(aURI, uriString, nullptr);
715 : }
716 :
717 : bool
718 3 : nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString& aUrl)
719 : {
720 : // Oh dear, the protocol is invalid. Test if the protocol might
721 : // actually be a url without a protocol:
722 : //
723 : // http://www.faqs.org/rfcs/rfc1738.html
724 : // http://www.faqs.org/rfcs/rfc2396.html
725 : //
726 : // e.g. Anything of the form:
727 : //
728 : // <hostname>:<port> or
729 : // <hostname>:<port>/
730 : //
731 : // Where <hostname> is a string of alphanumeric characters and dashes
732 : // separated by dots.
733 : // and <port> is a 5 or less digits. This actually breaks the rfc2396
734 : // definition of a scheme which allows dots in schemes.
735 : //
736 : // Note:
737 : // People expecting this to work with
738 : // <user>:<password>@<host>:<port>/<url-path> will be disappointed!
739 : //
740 : // Note: Parser could be a lot tighter, tossing out silly hostnames
741 : // such as those containing consecutive dots and so on.
742 :
743 : // Read the hostname which should of the form
744 : // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
745 :
746 3 : nsACString::const_iterator iterBegin;
747 3 : nsACString::const_iterator iterEnd;
748 3 : aUrl.BeginReading(iterBegin);
749 3 : aUrl.EndReading(iterEnd);
750 3 : nsACString::const_iterator iter = iterBegin;
751 :
752 3 : while (iter != iterEnd) {
753 3 : uint32_t chunkSize = 0;
754 : // Parse a chunk of the address
755 114 : while (iter != iterEnd &&
756 60 : (*iter == '-' ||
757 33 : nsCRT::IsAsciiAlpha(*iter) ||
758 3 : nsCRT::IsAsciiDigit(*iter))) {
759 27 : ++chunkSize;
760 27 : ++iter;
761 : }
762 3 : if (chunkSize == 0 || iter == iterEnd) {
763 0 : return false;
764 : }
765 3 : if (*iter == ':') {
766 : // Go onto checking the for the digits
767 3 : break;
768 : }
769 0 : if (*iter != '.') {
770 : // Whatever it is, it ain't a hostname!
771 0 : return false;
772 : }
773 0 : ++iter;
774 : }
775 3 : if (iter == iterEnd) {
776 : // No point continuing since there is no colon
777 0 : return false;
778 : }
779 3 : ++iter;
780 :
781 : // Count the number of digits after the colon and before the
782 : // next forward slash (or end of string)
783 :
784 3 : uint32_t digitCount = 0;
785 33 : while (iter != iterEnd && digitCount <= 5) {
786 18 : if (nsCRT::IsAsciiDigit(*iter)) {
787 15 : digitCount++;
788 3 : } else if (*iter == '/') {
789 3 : break;
790 : } else {
791 : // Whatever it is, it ain't a port!
792 0 : return false;
793 : }
794 15 : ++iter;
795 : }
796 3 : if (digitCount == 0 || digitCount > 5) {
797 : // No digits or more digits than a port would have.
798 0 : return false;
799 : }
800 :
801 : // Yes, it's possibly a host:port url
802 3 : return true;
803 : }
804 :
805 : nsresult
806 3 : nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString,
807 : nsDefaultURIFixupInfo* aFixupInfo,
808 : nsIInputStream** aPostData)
809 : {
810 : // These are keyword formatted strings
811 : // "what is mozilla"
812 : // "what is mozilla?"
813 : // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
814 : // "?mozilla" - anything that begins with a question mark
815 : // "?site:mozilla.org docshell"
816 : // Things that have a quote before the first dot/colon
817 : // "mozilla" - checked against a whitelist to see if it's a host or not
818 : // ".mozilla", "mozilla." - ditto
819 :
820 : // These are not keyword formatted strings
821 : // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
822 : // "www.blah.com stuff"
823 : // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
824 : // "nonQualifiedHost:80 args"
825 : // "nonQualifiedHost?"
826 : // "nonQualifiedHost?args"
827 : // "nonQualifiedHost?some args"
828 : // "blah.com."
829 :
830 : // Note: uint32_t(kNotFound) is greater than any actual location
831 : // in practice. So if we cast all locations to uint32_t, then a <
832 : // b guarantees that either b is kNotFound and a is found, or both
833 : // are found and a found before b.
834 :
835 3 : uint32_t firstDotLoc = uint32_t(kNotFound);
836 3 : uint32_t lastDotLoc = uint32_t(kNotFound);
837 3 : uint32_t firstColonLoc = uint32_t(kNotFound);
838 3 : uint32_t firstQuoteLoc = uint32_t(kNotFound);
839 3 : uint32_t firstSpaceLoc = uint32_t(kNotFound);
840 3 : uint32_t firstQMarkLoc = uint32_t(kNotFound);
841 3 : uint32_t lastLSBracketLoc = uint32_t(kNotFound);
842 3 : uint32_t lastSlashLoc = uint32_t(kNotFound);
843 3 : uint32_t pos = 0;
844 3 : uint32_t foundDots = 0;
845 3 : uint32_t foundColons = 0;
846 3 : uint32_t foundDigits = 0;
847 3 : uint32_t foundRSBrackets = 0;
848 3 : bool looksLikeIpv6 = true;
849 3 : bool hasAsciiAlpha = false;
850 :
851 3 : nsACString::const_iterator iterBegin;
852 3 : nsACString::const_iterator iterEnd;
853 3 : aURIString.BeginReading(iterBegin);
854 3 : aURIString.EndReading(iterEnd);
855 3 : nsACString::const_iterator iter = iterBegin;
856 :
857 279 : while (iter != iterEnd) {
858 138 : if (pos >= 1 && foundRSBrackets == 0) {
859 270 : if (!(lastLSBracketLoc == 0 &&
860 0 : (*iter == ':' ||
861 0 : *iter == '.' ||
862 0 : *iter == ']' ||
863 0 : (*iter >= 'a' && *iter <= 'f') ||
864 0 : (*iter >= 'A' && *iter <= 'F') ||
865 0 : nsCRT::IsAsciiDigit(*iter)))) {
866 135 : looksLikeIpv6 = false;
867 : }
868 : }
869 :
870 : // If we're at the end of the string or this is the first slash,
871 : // check if the thing before the slash looks like ipv4:
872 411 : if ((iterEnd - iter == 1 ||
873 48 : (lastSlashLoc == uint32_t(kNotFound) && *iter == '/')) &&
874 : // Need 2 or 3 dots + only digits
875 144 : (foundDots == 2 || foundDots == 3) &&
876 : // and they should be all that came before now:
877 0 : (foundDots + foundDigits == pos ||
878 : // or maybe there was also exactly 1 colon that came after the last dot,
879 : // and the digits, dots and colon were all that came before now:
880 0 : (foundColons == 1 && firstColonLoc > lastDotLoc &&
881 0 : foundDots + foundDigits + foundColons == pos))) {
882 : // Hurray, we got ourselves some ipv4!
883 : // At this point, there's no way we will do a keyword lookup, so just bail immediately:
884 0 : return NS_OK;
885 : }
886 :
887 138 : if (*iter == '.') {
888 3 : ++foundDots;
889 3 : lastDotLoc = pos;
890 3 : if (firstDotLoc == uint32_t(kNotFound)) {
891 3 : firstDotLoc = pos;
892 : }
893 135 : } else if (*iter == ':') {
894 3 : ++foundColons;
895 3 : if (firstColonLoc == uint32_t(kNotFound)) {
896 3 : firstColonLoc = pos;
897 : }
898 132 : } else if (*iter == ' ' && firstSpaceLoc == uint32_t(kNotFound)) {
899 0 : firstSpaceLoc = pos;
900 132 : } else if (*iter == '?' && firstQMarkLoc == uint32_t(kNotFound)) {
901 0 : firstQMarkLoc = pos;
902 132 : } else if ((*iter == '\'' || *iter == '"') &&
903 : firstQuoteLoc == uint32_t(kNotFound)) {
904 0 : firstQuoteLoc = pos;
905 132 : } else if (*iter == '[') {
906 0 : lastLSBracketLoc = pos;
907 132 : } else if (*iter == ']') {
908 0 : foundRSBrackets++;
909 132 : } else if (*iter == '/') {
910 6 : lastSlashLoc = pos;
911 126 : } else if (nsCRT::IsAsciiAlpha(*iter)) {
912 105 : hasAsciiAlpha = true;
913 21 : } else if (nsCRT::IsAsciiDigit(*iter)) {
914 15 : ++foundDigits;
915 : }
916 :
917 138 : pos++;
918 138 : iter++;
919 : }
920 :
921 3 : if (lastLSBracketLoc > 0 || foundRSBrackets != 1) {
922 3 : looksLikeIpv6 = false;
923 : }
924 :
925 : // If there are only colons and only hexadecimal characters ([a-z][0-9])
926 : // enclosed in [], then don't do a keyword lookup
927 3 : if (looksLikeIpv6) {
928 0 : return NS_OK;
929 : }
930 :
931 6 : nsAutoCString asciiHost;
932 6 : nsAutoCString host;
933 :
934 : bool isValidAsciiHost =
935 3 : aFixupInfo->mFixedURI &&
936 6 : NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
937 6 : !asciiHost.IsEmpty();
938 :
939 : bool isValidHost =
940 3 : aFixupInfo->mFixedURI &&
941 6 : NS_SUCCEEDED(aFixupInfo->mFixedURI->GetHost(host)) &&
942 6 : !host.IsEmpty();
943 :
944 3 : nsresult rv = NS_OK;
945 : // We do keyword lookups if a space or quote preceded the dot, colon
946 : // or question mark (or if the latter is not found, or if the input starts
947 : // with a question mark)
948 3 : if (((firstSpaceLoc < firstDotLoc || firstQuoteLoc < firstDotLoc) &&
949 0 : (firstSpaceLoc < firstColonLoc || firstQuoteLoc < firstColonLoc) &&
950 0 : (firstSpaceLoc < firstQMarkLoc || firstQuoteLoc < firstQMarkLoc)) ||
951 : firstQMarkLoc == 0) {
952 0 : rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
953 0 : aPostData);
954 : // ... or when the host is the same as asciiHost and there are no
955 : // characters from [a-z][A-Z]
956 3 : } else if (isValidAsciiHost && isValidHost && !hasAsciiAlpha &&
957 0 : host.EqualsIgnoreCase(asciiHost.get())) {
958 0 : if (!sDNSFirstForSingleWords) {
959 0 : rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
960 0 : aPostData);
961 : }
962 : }
963 : // ... or if there is no question mark or colon, and there is either no
964 : // dot, or exactly 1 and it is the first or last character of the input:
965 6 : else if ((firstDotLoc == uint32_t(kNotFound) ||
966 6 : (foundDots == 1 && (firstDotLoc == 0 ||
967 3 : firstDotLoc == aURIString.Length() - 1))) &&
968 3 : firstColonLoc == uint32_t(kNotFound) &&
969 : firstQMarkLoc == uint32_t(kNotFound)) {
970 0 : if (isValidAsciiHost && IsDomainWhitelisted(asciiHost, firstDotLoc)) {
971 0 : return NS_OK;
972 : }
973 :
974 : // ... unless there are no dots, and a slash, and alpha characters, and
975 : // this is a valid host:
976 0 : if (firstDotLoc == uint32_t(kNotFound) &&
977 0 : lastSlashLoc != uint32_t(kNotFound) &&
978 0 : hasAsciiAlpha && isValidAsciiHost) {
979 0 : return NS_OK;
980 : }
981 :
982 : // If we get here, we don't have a valid URI, or we did but the
983 : // host is not whitelisted, so we do a keyword search *anyway*:
984 0 : rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
985 0 : aPostData);
986 : }
987 3 : return rv;
988 : }
989 :
990 : bool
991 0 : nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost,
992 : const uint32_t aDotLoc)
993 : {
994 0 : if (sDNSFirstForSingleWords) {
995 0 : return true;
996 : }
997 : // Check if this domain is whitelisted as an actual
998 : // domain (which will prevent a keyword query)
999 : // NB: any processing of the host here should stay in sync with
1000 : // code in the front-end(s) that set the pref.
1001 :
1002 0 : nsAutoCString pref("browser.fixup.domainwhitelist.");
1003 :
1004 0 : if (aDotLoc == aAsciiHost.Length() - 1) {
1005 0 : pref.Append(Substring(aAsciiHost, 0, aAsciiHost.Length() - 1));
1006 : } else {
1007 0 : pref.Append(aAsciiHost);
1008 : }
1009 :
1010 0 : return Preferences::GetBool(pref.get(), false);
1011 : }
1012 :
1013 : NS_IMETHODIMP
1014 0 : nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain,
1015 : const uint32_t aDotLoc,
1016 : bool* aResult)
1017 : {
1018 0 : *aResult = IsDomainWhitelisted(aDomain, aDotLoc);
1019 0 : return NS_OK;
1020 : }
1021 :
1022 : /* Implementation of nsIURIFixupInfo */
1023 81 : NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
1024 :
1025 9 : nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput)
1026 : : mFixupChangedProtocol(false)
1027 9 : , mFixupCreatedAlternateURI(false)
1028 : {
1029 9 : mOriginalInput = aOriginalInput;
1030 9 : }
1031 :
1032 14 : nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
1033 : {
1034 21 : }
1035 :
1036 : NS_IMETHODIMP
1037 0 : nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
1038 : {
1039 0 : *aConsumer = mConsumer;
1040 0 : NS_IF_ADDREF(*aConsumer);
1041 0 : return NS_OK;
1042 : }
1043 :
1044 : NS_IMETHODIMP
1045 3 : nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
1046 : {
1047 3 : mConsumer = aConsumer;
1048 3 : return NS_OK;
1049 : }
1050 :
1051 : NS_IMETHODIMP
1052 7 : nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
1053 : {
1054 7 : *aPreferredURI = mPreferredURI;
1055 7 : NS_IF_ADDREF(*aPreferredURI);
1056 7 : return NS_OK;
1057 : }
1058 :
1059 : NS_IMETHODIMP
1060 8 : nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
1061 : {
1062 8 : *aFixedURI = mFixedURI;
1063 8 : NS_IF_ADDREF(*aFixedURI);
1064 8 : return NS_OK;
1065 : }
1066 :
1067 : NS_IMETHODIMP
1068 5 : nsDefaultURIFixupInfo::GetKeywordProviderName(nsAString& aResult)
1069 : {
1070 5 : aResult = mKeywordProviderName;
1071 5 : return NS_OK;
1072 : }
1073 :
1074 : NS_IMETHODIMP
1075 3 : nsDefaultURIFixupInfo::GetKeywordAsSent(nsAString& aResult)
1076 : {
1077 3 : aResult = mKeywordAsSent;
1078 3 : return NS_OK;
1079 : }
1080 :
1081 : NS_IMETHODIMP
1082 0 : nsDefaultURIFixupInfo::GetFixupChangedProtocol(bool* aResult)
1083 : {
1084 0 : *aResult = mFixupChangedProtocol;
1085 0 : return NS_OK;
1086 : }
1087 :
1088 : NS_IMETHODIMP
1089 0 : nsDefaultURIFixupInfo::GetFixupCreatedAlternateURI(bool* aResult)
1090 : {
1091 0 : *aResult = mFixupCreatedAlternateURI;
1092 0 : return NS_OK;
1093 : }
1094 :
1095 : NS_IMETHODIMP
1096 0 : nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aResult)
1097 : {
1098 0 : aResult = mOriginalInput;
1099 0 : return NS_OK;
1100 : }
|