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