This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.fsresource-2.1.8 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-fsresource.git
commit e17a9b8afd93a72f48fd6a4d47800985fce83d00 Author: Stefan Seifert <[email protected]> AuthorDate: Wed Jul 12 16:46:23 2017 +0000 SLING-7007 fsresource: Support URL-encoded file names git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/fsresource@1801752 13f79535-47bb-0310-9956-ffa450edef68 --- .../internal/mapper/ContentFileResourceMapper.java | 2 +- .../sling/fsprovider/internal/mapper/Escape.java | 104 +++++++++++++++++++++ .../internal/mapper/FileResourceMapper.java | 4 +- .../sling/fsprovider/internal/FilesFolderTest.java | 5 +- .../sling/fsprovider/internal/JcrMixedTest.java | 4 +- .../fsprovider/internal/JcrXmlContentTest.java | 4 +- .../sling/fsprovider/internal/JsonContentTest.java | 11 ++- .../sling/fsprovider/internal/TestUtils.java | 4 +- .../fsprovider/internal/mapper/EscapeTest.java | 46 +++++++++ .../folder1/{file1b.txt => sling%3Afile1b.txt} | 0 .../{content2.json => sling%3Acontent2.json} | 0 11 files changed, 171 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java index 20739d6..bbf2af6 100644 --- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java +++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java @@ -131,7 +131,7 @@ public final class ContentFileResourceMapper implements FsResourceMapper { if (!StringUtils.startsWith(path, providerRootPrefix)) { return null; } - String relPath = path.substring(providerRootPrefix.length()); + String relPath = Escape.resourceToFileName(path.substring(providerRootPrefix.length())); for (String filenameSuffix : contentFileExtensions.getSuffixes()) { File file = new File(providerFile, relPath + filenameSuffix); if (file.exists()) { diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/Escape.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/Escape.java new file mode 100644 index 0000000..b28280f --- /dev/null +++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/Escape.java @@ -0,0 +1,104 @@ +/* + * 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.sling.fsprovider.internal.mapper; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.BitSet; + +import org.apache.commons.lang3.CharEncoding; + +/** + * Manages deescaping for platform file names to resource names. + */ +public final class Escape { + + /** + * List of characters typically prohibited on unix and windows file systems. + * "/" is not included because it is neither allowed in resource nor in file names on any system. + */ + private static final char[] RESERVED_CHARS = { + '<', + '>', + ':', + '"', + '\\', + '|', + '?', + '*', + 0x00 + }; + private static final BitSet RESERVED_CHARS_SET = new BitSet(); + static { + for (int i=0; i<RESERVED_CHARS.length; i++) { + RESERVED_CHARS_SET.set(RESERVED_CHARS[i]); + } + } + + private Escape() { + // static methods only + } + + /** + * Convert file name to resource name. + * Applies same rules as Apache Sling JCR ContentLoader. + * @param path File name or path + * @return Resource name or path + */ + public static String fileToResourceName(String path) { + // check for encoded characters (%xx) + // has encoded characters, need to decode + if (path.indexOf('%') >= 0) { + try { + return URLDecoder.decode(path, "UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException("Unsupported encoding.", ex); + } + } + return path; + } + + /** + * Converts resource name to file name. + * Allows all characters, but URL-encodes characters that are in the list of {@link #RESERVED_CHARS}. + * @param name Resource name or path + * @return File name or path + */ + public static String resourceToFileName(String path) { + try { + StringBuilder result = new StringBuilder(); + for (int i=0; i<path.length(); i++) { + char c = path.charAt(i); + if (RESERVED_CHARS_SET.get(c)) { + result.append(URLEncoder.encode(String.valueOf(c), CharEncoding.UTF_8)); + } + else { + result.append(c); + } + } + return result.toString(); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException("Unsupported encoding.", ex); + } + } + +} diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java index ec9b650..e0a1429 100644 --- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java +++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java @@ -118,7 +118,7 @@ public final class FileResourceMapper implements FsResourceMapper { @Override public Object transform(Object input) { File file = (File)input; - String path = parentPath + "/" + file.getName(); + String path = parentPath + "/" + Escape.fileToResourceName(file.getName()); return new FileResource(resolver, path, file, contentFileExtensions, contentFileCache); } }); @@ -137,7 +137,7 @@ public final class FileResourceMapper implements FsResourceMapper { return providerFile; } if (path.startsWith(providerRootPrefix)) { - String relPath = path.substring(providerRootPrefix.length()); + String relPath = Escape.resourceToFileName(path.substring(providerRootPrefix.length())); File file = new File(providerFile, relPath); if (file.exists() && !contentFileExtensions.matchesSuffix(file)) { return file; diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java index c1f3470..7ebc089 100644 --- a/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java +++ b/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java @@ -63,10 +63,11 @@ public class FilesFolderTest { @Test public void testFiles() { assertFile(fsroot, "folder1/file1a.txt", "file1a"); - assertFile(fsroot, "folder1/file1b.txt", "file1b"); + assertFile(fsroot, "folder1/sling:file1b.txt", "file1b"); assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a"); assertFile(fsroot, "folder2/content.json", null); assertFile(fsroot, "folder2/content/file2content.txt", "file2content"); + assertFile(fsroot, "folder2/content/sling:content2.json", null); assertFile(fsroot, "folder3/content.jcr.xml", null); } @@ -74,7 +75,7 @@ public class FilesFolderTest { public void testListChildren() { assertThat(root, ResourceMatchers.containsChildren("fs-test")); assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2", "folder3")); - assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "file1b.txt")); + assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "sling:file1b.txt")); assertThat(fsroot.getChild("folder2"), ResourceMatchers.hasChildren("folder21", "content.json")); assertFalse(fsroot.getChild("folder1/file1a.txt").listChildren().hasNext()); } diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java index 9a220d9..b3e4d3b 100644 --- a/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java +++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java @@ -82,7 +82,7 @@ public class JcrMixedTest { @Test public void testFiles() { assertFile(fsroot, "folder1/file1a.txt", "file1a"); - assertFile(fsroot, "folder1/file1b.txt", "file1b"); + assertFile(fsroot, "folder1/sling:file1b.txt", "file1b"); assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a"); assertFile(fsroot, "folder2/content.json", null); @@ -97,7 +97,7 @@ public class JcrMixedTest { public void testListChildren() { assertThat(root, ResourceMatchers.containsChildren("fs-test")); assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2", "folder99")); - assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("file1a.txt", "file1b.txt", "file1c.txt")); + assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("file1a.txt", "sling:file1b.txt", "file1c.txt")); } } diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java index cece206..1b8e2a9 100644 --- a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java +++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java @@ -79,7 +79,7 @@ public class JcrXmlContentTest { @Test public void testFiles() { assertFile(fsroot, "folder1/file1a.txt", "file1a"); - assertFile(fsroot, "folder1/file1b.txt", "file1b"); + assertFile(fsroot, "folder1/sling:file1b.txt", "file1b"); assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a"); assertFile(fsroot, "folder2/content.json", null); assertNull(fsroot.getChild("folder3/content.jcr.xml")); @@ -89,7 +89,7 @@ public class JcrXmlContentTest { public void testListChildren() { assertThat(root, ResourceMatchers.containsChildren("fs-test")); assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2")); - assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "file1b.txt")); + assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "sling:file1b.txt")); assertThat(fsroot.getChild("folder2"), ResourceMatchers.hasChildren("folder21", "content")); } diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java index 4fb6a32..eb4276e 100644 --- a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java +++ b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java @@ -89,7 +89,7 @@ public class JsonContentTest { @Test public void testFiles() { assertFile(fsroot, "folder1/file1a.txt", "file1a"); - assertFile(fsroot, "folder1/file1b.txt", "file1b"); + assertFile(fsroot, "folder1/sling:file1b.txt", "file1b"); assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a"); assertNull(fsroot.getChild("folder2/content.json")); assertFile(fsroot, "folder2/content/file2content.txt", "file2content"); @@ -100,7 +100,7 @@ public class JsonContentTest { public void testListChildren() { assertThat(root, ResourceMatchers.containsChildren("fs-test")); assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2")); - assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "file1b.txt")); + assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "sling:file1b.txt")); assertThat(fsroot.getChild("folder2"), ResourceMatchers.hasChildren("folder21", "content")); } @@ -282,4 +282,11 @@ public class JsonContentTest { assertEquals("en", node.getProperty("jcr:language").getString()); } + @Test + public void testContent2() throws RepositoryException { + Resource content2 = fsroot.getChild("folder2/content/sling:content2"); + assertNotNull(content2); + assertEquals("app:Page", content2.getResourceType()); + } + } diff --git a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java index edfbc68..5539a40 100644 --- a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java +++ b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java @@ -40,6 +40,7 @@ import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.observation.ResourceChange; import org.apache.sling.api.resource.observation.ResourceChange.ChangeType; import org.apache.sling.api.resource.observation.ResourceChangeListener; +import org.apache.sling.fsprovider.internal.mapper.Escape; import org.apache.sling.hamcrest.ResourceMatchers; import org.apache.sling.testing.mock.osgi.MapUtil; import org.apache.sling.testing.mock.osgi.context.AbstractContextPlugin; @@ -84,8 +85,7 @@ class TestUtils { assertEquals("nt:file", file.getResourceType()); assertNull(file.getResourceSuperType()); - assertEquals(file.getName(), file.adaptTo(File.class).getName()); - assertTrue(StringUtils.contains(file.adaptTo(URL.class).toString(), file.getName())); + assertEquals(file.getName(), Escape.fileToResourceName(file.adaptTo(File.class).getName())); if (content != null) { try { diff --git a/src/test/java/org/apache/sling/fsprovider/internal/mapper/EscapeTest.java b/src/test/java/org/apache/sling/fsprovider/internal/mapper/EscapeTest.java new file mode 100644 index 0000000..a44bda4 --- /dev/null +++ b/src/test/java/org/apache/sling/fsprovider/internal/mapper/EscapeTest.java @@ -0,0 +1,46 @@ +/* + * 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.sling.fsprovider.internal.mapper; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class EscapeTest { + + @Test + public void testFileToResourceName() { + assertEquals("abc", Escape.fileToResourceName("abc")); + assertEquals("abc.txt", Escape.fileToResourceName("abc.txt")); + assertEquals("xyz:abc.txt", Escape.fileToResourceName("xyz%3Aabc.txt")); + assertEquals("a<>:\"/\\|?*b", Escape.fileToResourceName("a%3C%3E%3A%22/%5C%7C%3F%2Ab")); + assertEquals("", Escape.fileToResourceName("")); + } + + @Test + public void testResourceToFileName() { + assertEquals("abc", Escape.resourceToFileName("abc")); + assertEquals("abc.txt", Escape.resourceToFileName("abc.txt")); + assertEquals("xyz%3Aabc.txt", Escape.resourceToFileName("xyz:abc.txt")); + // URLEncoder does not encode '*' + assertEquals("a%3C%3E%3A%22/%5C%7C%3F*b", Escape.resourceToFileName("a<>:\"/\\|?*b")); + assertEquals("", Escape.resourceToFileName("")); + } + +} diff --git a/src/test/resources/fs-test/folder1/file1b.txt b/src/test/resources/fs-test/folder1/sling%3Afile1b.txt similarity index 100% rename from src/test/resources/fs-test/folder1/file1b.txt rename to src/test/resources/fs-test/folder1/sling%3Afile1b.txt diff --git a/src/test/resources/fs-test/folder2/content/content2.json b/src/test/resources/fs-test/folder2/content/sling%3Acontent2.json similarity index 100% rename from src/test/resources/fs-test/folder2/content/content2.json rename to src/test/resources/fs-test/folder2/content/sling%3Acontent2.json -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
