Re: leading space char in smb file name
Jim Hurne <[email protected]>
| Newsgroups | gmane.network.samba.java |
|---|---|
| Message-ID | <[email protected]> |
Hello Michael, We ran into this problem as well. We also ran into similar problems with filenames starting with with ".#" and "..#". To fix the problems, we ended up modifying the jcifs.smb.Handler class which is responsible for parsing smb URLs. Our changes are included in the attached patch. The two issues are a direct result of the differences between HTTP URLs and "smb" URLs. The jcifs.smb.Handler class extends the java.net.URLStreamHandler class. The jcifs.smb.Handler class delegates a lot of processing to the URLStreamHandler class. however, the URLStreamHandler class parses URLs per the various HTTP specifications. which isn't always appropriate for SMB URLs. Thus, the call to super.parseURL is wrapped in code which tries to modify the output to match what you would expect for SMB URLs. To fix the leading whitespace issue, we updated the jcifs.smb.Handler class to check the original input (the spec variable) after the call to super.parseURL to see if it contained leading whitespace. If it does, then the leading whitespace is added back into the path component of the URL. I know Mike is hesitant to apply the patch to the official JCIFS distribution because this is a rather sensitive part of the library (parsing URLs affects everything). However, we've been using it in production for a few years in a variety of environments (large and small) without any problems. I've also attached a JUnit 4 test class we used to test the modifications. Nonetheless, the patch comes with no warranty and we cannot be held responsible if it doesn't work as expected or causes other problems. Regards, Jim Hurne | Software Engineer Office: +1.412.422.2499 1710 Murray Avenue, Pittsburgh PA 15217 USA [email protected] | Connect: www.vivisimo.com Vivisimo - Information Optimized On 04/13/2012 10:07 PM, Michael B Allen wrote: > On Wed, Apr 11, 2012 at 5:21 PM, Anonymous > <[email protected]> wrote: >> Hi, >> I had a problem with the SmbFile. >> I had a file with name "smb://server/share/folder/ foo.bar" on a smb server ( foo.bar with leading blank). This file was created with jcifs, but calling >> new SmbFile("smb://server/share/folder/").listFiles[0].getName() only returns "foo.bar" with no leading blank. >> >> Before I had this problem, I didn't believe that it is possible to have files with leading blanks... >> >> I've analyzed the problem, and figured out, that everything is transfered correct from the server to jcifs. >> In the internal variable unc the path is stored with blank, but in canon, what is used for get name it's not. >> >> I hope that helps improving jcifs > Hi Michael, > > This is a known problem. It is the java.net.URL class that is doing > it. When the listFiles routine calls the SmbFile constructor the name > is passed to the URL class which is trimming the space. There are > other characters that are known to cause problems like this such as # > at the end of names. There's not much we can do about it other than to > dump the java.net.URL class at some point but that would require a > complete re-write. > > Note that if you use an SmbFilenameFilter you can get to the > un-trimmed filename. But I don't know if that will help you because I > don't know if you will be able to create an SmbFile instance with it. > > Mike >
leading-space-or-dots.patch
(text/x-patch, 2.6 KB)
--- a/src/jcifs/smb/Handler.java
+++ b/src/jcifs/smb/Handler.java
@@ -21,12 +21,16 @@ package jcifs.smb;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.PrintStream;
public class Handler extends URLStreamHandler {
+ private static final Pattern DOT_POUND_PATTERN = Pattern.compile("(\\.*)#");
+
static final URLStreamHandler SMB_HANDLER = new Handler();
protected int getDefaultPort() {
@@ -48,12 +52,41 @@ public class Handler extends URLStreamHandler {
spec = "//" + spec;
limit += 2;
}
- super.parseURL( u, spec, start, limit );
+
+ String mSpec = spec;
+ Matcher matcher = DOT_POUND_PATTERN.matcher(spec);
+ String dots = "";
+ if (matcher.find()) {
+ dots = matcher.group(1);
+ mSpec = matcher.replaceFirst("#");
+ limit -= dots.length();
+ }
+
+ super.parseURL( u, mSpec, start, limit );
+
path = u.getPath();
ref = u.getRef();
+
+ // Add leading periods (".") into the path if they were preceded by a "#"
+ if (spec.contains(".#" + ref)) {
+ path += dots;
+ }
if (ref != null) {
path += '#' + ref;
}
+
+ if(hasLeadingWhitespace(spec)) {
+ //Add leading whitespace back into the path
+ int specIdxInPath = path.lastIndexOf(spec.trim());
+ StringBuffer pathBuilder = new StringBuffer(path);
+ for(int i = 0; i < spec.length() && isWhitespace(spec.charAt(i)); i++) {
+ char whitespaceChar = spec.charAt(i);
+ int numInserted = i;
+ pathBuilder.insert(specIdxInPath + numInserted, whitespaceChar);
+ }
+ path = pathBuilder.toString();
+ }
+
port = u.getPort();
if( port == -1 ) {
port = getDefaultPort();
@@ -62,4 +95,14 @@ public class Handler extends URLStreamHandler {
u.getAuthority(), u.getUserInfo(),
path, u.getQuery(), null );
}
+
+ private static boolean hasLeadingWhitespace(String string) {
+ return isWhitespace(string.charAt(0));
+ }
+
+ private static boolean isWhitespace(char c) {
+ // As per the Javadoc for String.trim(), "whitespace" is any character
+ //with a unicode value less than or equal to '\u0020'
+ return c <='\u0020';
+ }
}
SmbUrlTest.java
(text/x-java, 8.8 KB)
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class SmbUrlTest {
private static final String CONTEXT_PATH = "smb://some-share/test/dir/";
private String child;
private String expected;
@Parameters
public static Collection<Object[]> parameters() throws Exception {
return Arrays.asList(new Object[][] {
// Normal file names
{ "smb://", "smb://" },
{ "smb://some:23345/share", "smb://some:23345/share" },
{ "normal.txt", "smb://some-share/test/dir/normal.txt" },
{ "/normal.txt", "smb://some-share/normal.txt" },
{ "subdir/", "smb://some-share/test/dir/subdir/" },
{ "/subdir/", "smb://some-share/subdir/" },
{ "subdir/normal.txt", "smb://some-share/test/dir/subdir/normal.txt" },
// File names with hashes
{ "no#rmal.txt", "smb://some-share/test/dir/no#rmal.txt" },
{ "#subdir/", "smb://some-share/test/dir/#subdir/" },
{ "su#bdir/", "smb://some-share/test/dir/su#bdir/" },
{ "subdir/#normal.txt", "smb://some-share/test/dir/subdir/#normal.txt" },
// File names with dots and hashes
{ ".#onedot", "smb://some-share/test/dir/.#onedot" },
{ "..#twodots", "smb://some-share/test/dir/..#twodots" },
{ "...#threedots", "smb://some-share/test/dir/...#threedots" },
{ "....#fourdots", "smb://some-share/test/dir/....#fourdots" },
{ ".....#fivedots", "smb://some-share/test/dir/.....#fivedots" },
{ "......#sixdots", "smb://some-share/test/dir/......#sixdots" },
{ ".#onedot/", "smb://some-share/test/dir/.#onedot/" },
{ "..#twodots/", "smb://some-share/test/dir/..#twodots/" },
{ "/.#onedot", "smb://some-share/.#onedot" },
{ "/..#twodots", "smb://some-share/..#twodots" },
{ "prefix..#twodots", "smb://some-share/test/dir/prefix..#twodots" },
{ "subdir/.#onedot", "smb://some-share/test/dir/subdir/.#onedot" },
{ "subdir/..#twodots", "smb://some-share/test/dir/subdir/..#twodots" },
{ "subdir/.#onedot/another-dir/", "smb://some-share/test/dir/subdir/.#onedot/another-dir/" },
{ "subdir/..#twodots/another-dir/", "smb://some-share/test/dir/subdir/..#twodots/another-dir/" },
{ ".#onedot/.#onedot", "smb://some-share/test/dir/.#onedot/.#onedot" },
{ "..#twodots/..#twodots", "smb://some-share/test/dir/..#twodots/..#twodots" },
{ "..#twodots/.#onedot", "smb://some-share/test/dir/..#twodots/.#onedot" },
{ ".#onedot/..#twodots", "smb://some-share/test/dir/.#onedot/..#twodots" },
{ "../.#onedot", "smb://some-share/test/.#onedot" },
{ "../..#twodots", "smb://some-share/test/..#twodots" },
{ "./.#onedot", "smb://some-share/test/dir/.#onedot" },
{ "./..#twodots", "smb://some-share/test/dir/..#twodots" },
{ "smb://share/.#onedot", "smb://share/.#onedot" },
{ "smb://share/..#twodots", "smb://share/..#twodots" },
{ "smb://share:65674/.#onedot", "smb://share:65674/.#onedot" },
{ "smb://share:65674/..#twodots", "smb://share:65674/..#twodots" },
{ "smb://share/test/dir/.#onedot", "smb://share/test/dir/.#onedot" },
{ "smb://share/test/dir/..#twodots", "smb://share/test/dir/..#twodots" },
{ "smb://share:65674/test/dir/.#onedot", "smb://share:65674/test/dir/.#onedot" },
{ "smb://share:65674/test/dir/..#twodots", "smb://share:65674/test/dir/..#twodots" },
// File names with whitespace
{ " onespace", "smb://some-share/test/dir/ onespace" },
{ " twospace", "smb://some-share/test/dir/ twospace" },
{ "\tonetab", "smb://some-share/test/dir/\tonetab" },
{ "\t\ttwotab", "smb://some-share/test/dir/\t\ttwotab" },
{ "one space", "smb://some-share/test/dir/one space" },
{ "two spaces", "smb://some-share/test/dir/two spaces" },
{ "one\ttab", "smb://some-share/test/dir/one\ttab" },
{ "two\t\ttab", "smb://some-share/test/dir/two\t\ttab" },
{ "\t tabspace", "smb://some-share/test/dir/\t tabspace" },
{ " \tspacetab", "smb://some-share/test/dir/ \tspacetab" },
{ " onespace/another-dir/", "smb://some-share/test/dir/ onespace/another-dir/" },
{ " twospace/another-dir/", "smb://some-share/test/dir/ twospace/another-dir/" },
{ "\tonetab/another-dir/", "smb://some-share/test/dir/\tonetab/another-dir/" },
{ "\t\ttwotab/another-dir/", "smb://some-share/test/dir/\t\ttwotab/another-dir/" },
{ "one space/another-dir/", "smb://some-share/test/dir/one space/another-dir/" },
{ "two spaces/another-dir/", "smb://some-share/test/dir/two spaces/another-dir/" },
{ "one\ttab/another-dir/", "smb://some-share/test/dir/one\ttab/another-dir/" },
{ "two\t\ttab/another-dir/", "smb://some-share/test/dir/two\t\ttab/another-dir/" },
{ "\t tabspace/another-dir/", "smb://some-share/test/dir/\t tabspace/another-dir/" },
{ " \tspacetab/another-dir/", "smb://some-share/test/dir/ \tspacetab/another-dir/" },
{ "smb://share:65674/test/dir/ onespace", "smb://share:65674/test/dir/ onespace" },
{ "smb://share:65674/test/dir/ twospace", "smb://share:65674/test/dir/ twospace" },
{ "smb://share:65674/test/dir/\tonetab", "smb://share:65674/test/dir/\tonetab" },
{ "smb://share:65674/test/dir/\t\ttwotab", "smb://share:65674/test/dir/\t\ttwotab" },
// File names with whitespace and dots and hashes
{ ". onespace-onedot", "smb://some-share/test/dir/. onespace-onedot" },
{ ". twospace-onedot", "smb://some-share/test/dir/. twospace-onedot" },
{ ".\tonetab-onedot", "smb://some-share/test/dir/.\tonetab-onedot" },
{ ".\t\ttwotab-onedot", "smb://some-share/test/dir/.\t\ttwotab-onedot" },
{ ".# onespace-onedot", "smb://some-share/test/dir/.# onespace-onedot" },
{ ".# twospace-onedot", "smb://some-share/test/dir/.# twospace-onedot" },
{ ".#\tonetab-onedot", "smb://some-share/test/dir/.#\tonetab-onedot" },
{ ".#\t\ttwotab-onedot", "smb://some-share/test/dir/.#\t\ttwotab-onedot" },
{ ".. onespace-twodots", "smb://some-share/test/dir/.. onespace-twodots" },
{ ".. twospace-twodots", "smb://some-share/test/dir/.. twospace-twodots" },
{ "..\tonetab-twodots", "smb://some-share/test/dir/..\tonetab-twodots" },
{ "..\t\ttwotab-twodots", "smb://some-share/test/dir/..\t\ttwotab-twodots" },
{ "..# onespace-twodots", "smb://some-share/test/dir/..# onespace-twodots" },
{ "..# twospace-twodots", "smb://some-share/test/dir/..# twospace-twodots" },
{ "..#\tonetab-twodots", "smb://some-share/test/dir/..#\tonetab-twodots" },
{ "..#\t\ttwotab-twodots", "smb://some-share/test/dir/..#\t\ttwotab-twodots" },
{ "# onespace-hash", "smb://some-share/test/dir/# onespace-hash" },
{ "# twospace-hash", "smb://some-share/test/dir/# twospace-hash" },
{ "#\tonetab-hash", "smb://some-share/test/dir/#\tonetab-hash" },
{ "#\t\ttwotab-hash", "smb://some-share/test/dir/#\t\ttwotab-hash" }, });
}
@BeforeClass
public static void registerSmbProtocol() {
jcifs.Config.registerSmbURLHandler();
}
public SmbUrlTest(String child, String expected) throws MalformedURLException {
this.child = child;
this.expected = expected;
}
@Test
public void constructsUrls() throws Exception {
// This test is only valid for data that starts with "smb:"
if (child.contains("smb:")) {
URL newUrl = new URL(child);
assertThat(newUrl.toString(), is(expected));
}
}
@Test
public void constructsUrlsWithContext() throws Exception {
URL context = new URL(CONTEXT_PATH);
URL newUrl = new URL(context, child);
assertThat(newUrl.toString(), is(expected));
}
}