This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
The following commit(s) were added to refs/heads/master by this push: new aa119bc [VFS-798] IllegalArgumentException: Bad escape for Chinese characters in FileObject path. aa119bc is described below commit aa119bc00863dda7bb7b6a42963a6bf0be2f3717 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat Mar 27 18:14:41 2021 -0400 [VFS-798] IllegalArgumentException: Bad escape for Chinese characters in FileObject path. Original report, initial test, and impetus provided Xenos Amess in #168. --- .../commons/vfs2/provider/local/LocalFile.java | 16 ++- .../commons/vfs2/provider/local/LocalFileName.java | 40 +++++++- .../commons/vfs2/provider/url/UrlFileObject.java | 17 ++-- .../org/apache/commons/vfs2/FileObjectTest.java | 109 +++++++++++++++++++++ src/changes/changes.xml | 3 + 5 files changed, 176 insertions(+), 9 deletions(-) diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java index d9f6c1d..68e04ce 100644 --- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java +++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java @@ -21,8 +21,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URI; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.Objects; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; @@ -70,7 +72,7 @@ public class LocalFile extends AbstractFileObject<LocalFileSystem> { file = new File(fileName); } } - + /** * Creates this folder. */ @@ -186,6 +188,7 @@ public class LocalFile extends AbstractFileObject<LocalFileSystem> { return false; } + @SuppressWarnings("resource") // unwrapping, not allocating final LocalFile destLocalFile = (LocalFile) FileObjectUtils.getAbstractFileObject(destFile); if (!exists() || !destLocalFile.exists()) { return false; @@ -228,6 +231,7 @@ public class LocalFile extends AbstractFileObject<LocalFileSystem> { */ @Override protected void doRename(final FileObject newFile) throws Exception { + @SuppressWarnings("resource") // unwrapping, not allocating final LocalFile newLocalFile = (LocalFile) FileObjectUtils.getAbstractFileObject(newFile); if (!file.renameTo(newLocalFile.getLocalFile())) { @@ -269,6 +273,16 @@ public class LocalFile extends AbstractFileObject<LocalFileSystem> { return file; } + @Override + public URI getURI() { + try { + doAttach(); + } catch (Exception e) { + throw new IllegalArgumentException(Objects.toString(file)); + } + return URI.create(getName().getURI()); + } + /** * Returns the URI of the file. * diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFileName.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFileName.java index ff86c02..3474094 100644 --- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFileName.java +++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFileName.java @@ -16,6 +16,10 @@ */ package org.apache.commons.vfs2.provider.local; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.apache.commons.vfs2.FileName; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileType; @@ -56,6 +60,38 @@ public class LocalFileName extends AbstractFileName { return new LocalFileName(getScheme(), rootFile, path, type); } + @Override + protected String createURI() { + final StringBuilder buffer = new StringBuilder(); + appendRootUri(buffer, true); + final URI tmpUri = getFilePath().toUri(); + buffer.append(tmpUri.getRawPath()); + final String query = tmpUri.getRawQuery(); + if (query != null) { + buffer.append('?'); + buffer.append(query); + } + final String fragment = tmpUri.getRawFragment(); + if (fragment != null) { + buffer.append('#'); + buffer.append(fragment); + } + return buffer.toString(); + } + + private String createUriDecoded() throws FileSystemException { + return UriParser.decode(getURI()); + } + + /** + * Gets the NIO file Path. + * + * @return the NIO file Path. + */ + private Path getFilePath() { + return Paths.get(getPath()); + } + /** * Returns the root file for this file. * @@ -73,9 +109,9 @@ public class LocalFileName extends AbstractFileName { @Override public String toString() { try { - return UriParser.decode(super.getURI()); + return createUriDecoded(); } catch (final FileSystemException e) { - return super.getURI(); + return getURI(); } } } diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/url/UrlFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/url/UrlFileObject.java index 8e45d8c..440ef93 100644 --- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/url/UrlFileObject.java +++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/url/UrlFileObject.java @@ -30,6 +30,8 @@ import org.apache.commons.vfs2.FileType; import org.apache.commons.vfs2.provider.AbstractFileName; import org.apache.commons.vfs2.provider.AbstractFileObject; import org.apache.commons.vfs2.provider.URLFileName; +import org.apache.commons.vfs2.provider.UriParser; +import org.apache.commons.vfs2.provider.local.LocalFileName; /** * A {@link org.apache.commons.vfs2.FileObject FileObject} implementation backed by a {@link URL}. @@ -54,17 +56,20 @@ public class UrlFileObject extends AbstractFileObject<UrlFileSystem> { @Override protected void doAttach() throws Exception { if (url == null) { - // url = new URL(getName().getURI()); url = createURL(getName()); } } - protected URL createURL(final FileName name) throws MalformedURLException, FileSystemException, URIException { - if (name instanceof URLFileName) { - final URLFileName urlName = (URLFileName) getName(); - + protected URL createURL(final FileName fileName) throws MalformedURLException, FileSystemException, URIException { + // TODO Forthcoming clean up... + if (fileName instanceof URLFileName) { + final URLFileName urlFileName = (URLFileName) getName(); // TODO: charset - return new URL(urlName.getURIEncoded(null)); + return new URL(urlFileName.getURIEncoded(null)); + } + if (fileName instanceof LocalFileName) { + // decode + return new URL(((LocalFileName) getName()).toString()); } return new URL(getName().getURI()); } diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/FileObjectTest.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/FileObjectTest.java new file mode 100644 index 0000000..ee325a0 --- /dev/null +++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/FileObjectTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.vfs2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; +import java.nio.file.Paths; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.function.FailableFunction; +import org.apache.commons.vfs2.impl.StandardFileSystemManager; +import org.junit.Test; + +public class FileObjectTest { + + private static final String REL_PATH_GREAT = "src/test/resources/test-data/好.txt"; + + private static final String REL_PATH_SPACE = "src/test/resources/test-data/1 1.txt"; + + /** + * Expected contents of test files. + */ + public static final String TEST_FILE_CONTENT = "aaa"; + + /** + * Test file paths. + */ + public static final String[] TEST_FILE_PATHS = new String[] { REL_PATH_SPACE, REL_PATH_GREAT }; + + private static StandardFileSystemManager loadFileSystemManager() throws FileSystemException { + StandardFileSystemManager fileSystemManager = new StandardFileSystemManager(); + fileSystemManager.setLogger(null); + fileSystemManager.init(); + fileSystemManager.setBaseFile(new File(System.getProperty("user.dir"))); + return fileSystemManager; + } + + private static File toFile2(FileObject fileObject) throws FileSystemException { + if (fileObject == null || !"file".equals(fileObject.getURL().getProtocol())) { + return null; + } + return new File(fileObject.getName().getPathDecoded()); + } + + @SuppressWarnings("resource") + private void testProviderGetPath(String relPathStr) throws URISyntaxException { + FileSystems.getDefault().provider().getPath(new URI(Paths.get(relPathStr).toAbsolutePath().toUri().toString())); + } + + @Test + public void testProviderGetPathGreat() throws URISyntaxException { + testProviderGetPath(REL_PATH_GREAT); + } + + @Test + public void testProviderGetPathSpace() throws URISyntaxException { + testProviderGetPath(REL_PATH_SPACE); + } + + @Test + public void testToFile() throws IOException { + testToFile(fileObject -> fileObject.getPath().toFile()); + } + + private void testToFile(FailableFunction<FileObject, File, IOException> function) throws IOException { + for (String testFilePath : TEST_FILE_PATHS) { + try (FileSystemManager fileSystemManager = loadFileSystemManager(); + FileObject fileObject = fileSystemManager.resolveFile(testFilePath);) { + assertNotNull(fileObject); + try (final FileContent content = fileObject.getContent(); + InputStream inputStream = content.getInputStream()) { + assertEquals(TEST_FILE_CONTENT, IOUtils.toString(inputStream, StandardCharsets.UTF_8)); + } + File file = function.apply(fileObject); + assertNotNull(file); + assertEquals(TEST_FILE_CONTENT, FileUtils.readFileToString(file, StandardCharsets.UTF_8)); + } + } + } + + @Test + public void testToFile2() throws IOException { + testToFile(FileObjectTest::toFile2); + } +} diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 23a87a2..734f234 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -56,6 +56,9 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="Boris Petrov, Gary Gregory, Max Kellermann"> Fix NPE when closing a stream from a different thread #167. See also #166. </action> + <action type="fix" dev="ggregory" issue="VFS-798" due-to="XenoAmess, Gary Gregory"> + IllegalArgumentException: Bad escape for Chinese characters in FileObject path #168. + </action> <!-- UPDATES --> <action type="update" dev="ggregory" due-to="Arturo Bernal"> Replace construction of FileInputStream and FileOutputStream objects with Files NIO APIs. #164.