提交 044c4ad1 编写于 作者: M mindless

[FIXED HUDSON-2793] Fix from HUDSON-2379 introduced a bug for artifact/workspace

filenames with spaces.
- Added new Util.rawEncode which encodes the right set of characters (based on
  RFC1738) and deprecated Util.encode which only encodes non-ASCII.  Unit test too.
- Change previous fix to use Util.rawEncode instead of URLEncoder.encode
- New fix: add h.xmlEscape call for each displayed filename in artifacts/workspace
  so & and < are encoded for HTML display
- Add all these fixes in Run/artifacts-index.jelly which wasn't included before


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@14355 71c3de6d-444a-0410-be80-ed276b4c234a
上级 cfa07b50
......@@ -34,6 +34,9 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
......@@ -554,7 +557,11 @@ public class Util {
/**
* Escapes non-ASCII characters in URL.
* @deprecated Only escapes non-ASCII but leaves other URL-unsafe characters.
* Util.rawEncode should generally be used instead, though be careful to pass only
* a single path component to that method (it will encode /, but this method does not).
*/
@Deprecated
public static String encode(String s) {
try {
boolean escaped = false;
......@@ -588,6 +595,53 @@ public class Util {
}
}
/**
* Encode a single path component for use in an HTTP URL.
* Escapes all non-ASCII, general unsafe (space and "#%<>[\]^`{|}~)
* and HTTP special characters (/;:?) as specified in RFC1738,
* plus backslash (Windows path separator).
* Note that slash(/) is encoded, so the given string should be a
* single path component used in constructing a URL.
* Method name inspired by PHP's rawencode.
*/
public static String rawEncode(String s) {
boolean escaped = false;
StringBuilder out = null;
CharsetEncoder enc = null;
CharBuffer buf = null;
char c;
for (int i = 0, m = s.length(); i < m; i++) {
c = s.charAt(i);
if ((c<64 || c>90) && (c<97 || c>122) && (c<38 || c>57 || c==47)
&& c!=61 && c!=36 && c!=33 && c!=95) {
if (!escaped) {
out = new StringBuilder(i + (m - i) * 3);
out.append(s.substring(0, i));
enc = Charset.forName("UTF-8").newEncoder();
buf = CharBuffer.allocate(1);
escaped = true;
}
// 1 char -> UTF8
buf.put(0,c);
buf.rewind();
try {
for (byte b : enc.encode(buf).array()) {
out.append('%');
out.append(toDigit((b >> 4) & 0xF));
out.append(toDigit(b & 0xF));
}
} catch (CharacterCodingException ex) { }
} else if (escaped) {
out.append(c);
}
}
return escaped ? out.toString() : s;
}
private static char toDigit(int n) {
return (char)(n < 10 ? '0' + n : 'A' + n - 10);
}
/**
* Surrounds by a single-quote.
*/
......@@ -596,7 +650,7 @@ public class Util {
}
/**
* Escapes HTML unsafe characters like &lt;, &amp;to the respective character entities.
* Escapes HTML unsafe characters like &lt;, &amp; to the respective character entities.
*/
public static String escape(String text) {
StringBuilder buf = new StringBuilder(text.length()+64);
......@@ -639,12 +693,6 @@ public class Util {
return buf.toString();
}
private static char toDigit(int n) {
char ch = Character.forDigit(n,16);
if(ch>='a') ch = (char)(ch-'a'+'A');
return ch;
}
/**
* Creates an empty file.
*/
......
......@@ -17,7 +17,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
......@@ -380,14 +379,14 @@ public final class DirectoryBrowserSupport {
Arrays.sort(files,new FileComparator());
for( File f : files ) {
Path p = new Path(URLEncoder.encode(f.getName(),"UTF-8"),f.getName(),f.isDirectory(),f.length(), f.canRead());
Path p = new Path(Util.rawEncode(f.getName()),f.getName(),f.isDirectory(),f.length(), f.canRead());
if(!f.isDirectory()) {
r.add(Collections.singletonList(p));
} else {
// find all empty intermediate directory
List<Path> l = new ArrayList<Path>();
l.add(p);
String relPath = URLEncoder.encode(f.getName(),"UTF-8");
String relPath = Util.rawEncode(f.getName());
while(true) {
// files that don't start with '.' qualify for 'meaningful files', nor SCM related files
File[] sub = f.listFiles(new FilenameFilter() {
......@@ -398,7 +397,7 @@ public final class DirectoryBrowserSupport {
if(sub==null || sub.length!=1 || !sub[0].isDirectory())
break;
f = sub[0];
relPath += '/'+URLEncoder.encode(f.getName(),"UTF-8");
relPath += '/'+Util.rawEncode(f.getName());
l.add(new Path(relPath,f.getName(),true,0, f.canRead()));
}
r.add(l);
......@@ -465,7 +464,7 @@ public final class DirectoryBrowserSupport {
buildPathList(baseDir, parent, pathList, href);
}
href.append(URLEncoder.encode(filePath.getName(),"UTF-8"));
href.append(Util.rawEncode(filePath.getName()));
if (filePath.isDirectory()) {
href.append("/");
}
......
......@@ -45,7 +45,6 @@ import java.io.Writer;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
......@@ -567,16 +566,16 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
private void addArtifacts( File dir, String path, String pathHref, List<Artifact> r ) {
String[] children = dir.list();
if(children==null) return;
for (String child : children) try {
for (String child : children) {
if(r.size()>CUTOFF)
return;
File sub = new File(dir, child);
if (sub.isDirectory()) {
addArtifacts(sub, path + child + '/', pathHref + URLEncoder.encode(child,"UTF-8") + '/', r);
addArtifacts(sub, path + child + '/', pathHref + Util.rawEncode(child) + '/', r);
} else {
r.add(new Artifact(path + child, pathHref + URLEncoder.encode(child,"UTF-8")));
r.add(new Artifact(path + child, pathHref + Util.rawEncode(child)));
}
} catch (UnsupportedEncodingException e) { /* Won't happen as UTF-8 is hardcoded */ }
}
}
private static final int CUTOFF = 17; // 0, 1,... 16, and then "too many"
......
......@@ -9,7 +9,7 @@
<form action="${backPath}" method="get">
<a href="${topPath}"><img src="${imagesURL}/48x48/${icon}" class="rootIcon" alt=""/></a>
<j:forEach var="p" items="${parentPath}">
<a href="${p.href}">${p.title}</a>
<a href="${p.href}">${h.xmlEscape(p.title)}</a>
/
</j:forEach>
<input type="text" name="pattern" value="${pattern}" />
......@@ -34,10 +34,10 @@
<j:choose>
<j:when test="${x.readable}">
<a href="${t.href}">${t.title}</a>
<a href="${t.href}">${h.xmlEscape(t.title)}</a>
</j:when>
<j:otherwise>
${t.title}
${h.xmlEscape(t.title)}
</j:otherwise>
</j:choose>
......@@ -54,7 +54,7 @@
<img src="${imagesURL}/16x16/fingerprint.gif" alt="fingerprint" />
</a>
<st:nbsp/>
<a href="${x.href}/*view*/">view</a>
<a href="${x.href}/*view*/">${%view}</a>
</j:if>
</td>
</j:if>
......
......@@ -3,13 +3,13 @@
<st:include page="sidepanel.jelly" />
<l:main-panel>
<t:buildCaption>
Build Artifacts
${%Build Artifacts}
</t:buildCaption>
<ul>
<j:forEach var="f" items="${it.artifacts}">
<li><a href="artifact/${f}">${f}</a></li>
<li><a href="artifact/${f.href}">${h.xmlEscape(f.displayPath)}</a></li>
</j:forEach>
</ul>
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
</j:jelly>
......@@ -14,7 +14,7 @@
<ul>
<j:forEach var="f" items="${artifacts}">
<li>
<a href="${baseURL}artifact/${f.href}">${f.displayPath}</a>
<a href="${baseURL}artifact/${f.href}">${h.xmlEscape(f.displayPath)}</a>
<st:nbsp/>
<a href="${baseURL}artifact/${f.href}/*fingerprint*/"><img src="${imagesURL}/16x16/fingerprint.gif" alt="[fingerprint]" /></a>
</li>
......
......@@ -67,6 +67,23 @@ public class UtilTest extends TestCase {
assertEquals(encoded, "http://hudson/job/Hudson%20Job");
}
/**
* Test the rawEncode() method.
*/
public void testRawEncode() {
String[] data = { // Alternating raw,encoded
"abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"01234567890!@$&*()-_=+',.", "01234567890!@$&*()-_=+',.",
" \"#%/:;<>?", "%20%22%23%25%2F%3A%3B%3C%3E%3F",
"[\\]^`{|}~", "%5B%5C%5D%5E%60%7B%7C%7D%7E",
"d\u00E9velopp\u00E9s", "d%C3%A9%00velopp%C3%A9%00s",
};
for (int i = 0; i < data.length; i += 2) {
assertEquals("test " + i, data[i + 1], Util.rawEncode(data[i]));
}
}
/**
* Test the tryParseNumber() method.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册