Bug 942791 - Ensure non-ASCII filenames are accessible in FTP directory listings. r=mayhemer
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Fri, 29 Nov 2013 23:37:57 +0900
changeset 172780 4c27e14de1af059fe3a16c49e0ac304f8273006b
parent 172779 0bd264d20f3fecb416abb25f9567e23b2140a30f
child 172781 f4a802140bc7ec319cb7ba2b69afe3f516e35306
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs942791
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 942791 - Ensure non-ASCII filenames are accessible in FTP directory listings. r=mayhemer
netwerk/streamconv/converters/nsIndexedToHTML.cpp
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIndexedToHTML.h"
+#include "mozilla/dom/EncodingUtils.h"
 #include "nsNetUtil.h"
 #include "netCore.h"
 #include "nsStringStream.h"
 #include "nsIFileURL.h"
 #include "nsEscape.h"
 #include "nsIDirIndex.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsURLHelper.h"
@@ -846,79 +847,94 @@ nsIndexedToHTML::OnIndexAvailable(nsIReq
     if (escapedShort.IsEmpty())
         escapedShort.Assign(escaped);
     nsMemory::Free(escaped);
 
     pushBuffer.AppendLiteral(" href=\"");
     nsXPIDLCString loc;
     aIndex->GetLocation(getter_Copies(loc));
 
-    if (!mTextToSubURI) {
-        mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
-        if (NS_FAILED(rv)) return rv;
-    }
-
     nsXPIDLCString encoding;
     rv = mParser->GetEncoding(getter_Copies(encoding));
     if (NS_FAILED(rv)) return rv;
 
-    nsXPIDLString unEscapeSpec;
-    rv = mTextToSubURI->UnEscapeAndConvert(encoding, loc,
-                                           getter_Copies(unEscapeSpec));
-    if (NS_FAILED(rv)) return rv;
+    // Don't byte-to-Unicode conversion here, it is lossy.
+    loc.SetLength(nsUnescapeCount(loc.BeginWriting()));
 
     // need to escape links
-    nsAutoCString escapeBuf;
-
-    NS_ConvertUTF16toUTF8 utf8UnEscapeSpec(unEscapeSpec);
+    nsAutoCString locEscaped;
 
     // Adding trailing slash helps to recognize whether the URL points to a file
     // or a directory (bug #214405).
-    if ((type == nsIDirIndex::TYPE_DIRECTORY) &&
-        (utf8UnEscapeSpec.Last() != '/')) {
-        utf8UnEscapeSpec.Append('/');
+    if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) {
+        loc.Append('/');
     }
 
     // now minimally re-escape the location...
     uint32_t escFlags;
     // for some protocols, we expect the location to be absolute.
     // if so, and if the location indeed appears to be a valid URI, then go
     // ahead and treat it like one.
     if (mExpectAbsLoc &&
-        NS_SUCCEEDED(net_ExtractURLScheme(utf8UnEscapeSpec, nullptr, nullptr, nullptr))) {
+        NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) {
         // escape as absolute 
         escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal;
     }
     else {
         // escape as relative
         // esc_Directory is needed because directories have a trailing slash.
         // Without it, the trailing '/' will be escaped, and links from within
         // that directory will be incorrect
         escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
     }
-    NS_EscapeURL(utf8UnEscapeSpec.get(), utf8UnEscapeSpec.Length(), escFlags, escapeBuf);
+    NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped);
     // esc_Directory does not escape the semicolons, so if a filename
     // contains semicolons we need to manually escape them.
     // This replacement should be removed in bug #473280
-    escapeBuf.ReplaceSubstring(";", "%3b");
-    NS_ConvertUTF8toUTF16 utf16URI(escapeBuf);
+    locEscaped.ReplaceSubstring(";", "%3b");
+    nsAutoString utf16URI;
+    if (encoding.EqualsLiteral("UTF-8")) {
+        // Try to convert non-ASCII bytes to Unicode using UTF-8 decoder.
+        nsCOMPtr<nsIUnicodeDecoder> decoder =
+            mozilla::dom::EncodingUtils::DecoderForEncoding("UTF-8");
+        decoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
+
+        int32_t len = locEscaped.Length();
+        int32_t outlen = 0;
+        rv = decoder->GetMaxLength(locEscaped.get(), len, &outlen);
+        if (NS_FAILED(rv)) {
+            return rv;
+        }
+        nsAutoArrayPtr<PRUnichar> outbuf(new PRUnichar[outlen]);
+        rv = decoder->Convert(locEscaped.get(), &len, outbuf, &outlen);
+        // Use the result only if the sequence is valid as UTF-8.
+        if (rv == NS_OK) {
+            utf16URI.Append(outbuf, outlen);
+        }
+    }
+    if (utf16URI.IsEmpty()) {
+        // Escape all non-ASCII bytes to preserve the raw value.
+        nsAutoCString outstr;
+        NS_EscapeURL(locEscaped, esc_AlwaysCopy | esc_OnlyNonASCII, outstr);
+        CopyASCIItoUTF16(outstr, utf16URI);
+    }
     nsString htmlEscapedURL;
     htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length()));
     pushBuffer.Append(htmlEscapedURL);
 
     pushBuffer.AppendLiteral("\">");
 
     if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) {
         pushBuffer.AppendLiteral("<img src=\"moz-icon://");
-        int32_t lastDot = escapeBuf.RFindChar('.');
+        int32_t lastDot = locEscaped.RFindChar('.');
         if (lastDot != kNotFound) {
-            escapeBuf.Cut(0, lastDot);
-            NS_ConvertUTF8toUTF16 utf16EscapeBuf(escapeBuf);
+            locEscaped.Cut(0, lastDot);
+            NS_ConvertUTF8toUTF16 utf16LocEscaped(locEscaped);
             nsString htmlFileExt;
-            htmlFileExt.Adopt(nsEscapeHTML2(utf16EscapeBuf.get(), utf16EscapeBuf.Length()));
+            htmlFileExt.Adopt(nsEscapeHTML2(utf16LocEscaped.get(), utf16LocEscaped.Length()));
             pushBuffer.Append(htmlFileExt);
         } else {
             pushBuffer.AppendLiteral("unknown");
         }
         pushBuffer.AppendLiteral("?size=16\" alt=\"");
 
         nsXPIDLString altText;
         rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirFileLabel").get(),