1 package hudson.model; 2 3 import hudson.FilePath; 4 import hudson.FilePath.FileCallable; 5 import hudson.remoting.VirtualChannel; 6 import org.kohsuke.stapler.StaplerRequest; 7 import org.kohsuke.stapler.StaplerResponse; 8 9 import javax.servlet.ServletException ; 10 import javax.servlet.http.HttpServletResponse ; 11 import java.io.File ; 12 import java.io.FilenameFilter ; 13 import java.io.IOException ; 14 import java.io.InputStream ; 15 import java.io.Serializable ; 16 import java.net.URLEncoder ; 17 import java.util.ArrayList ; 18 import java.util.Arrays ; 19 import java.util.Collections ; 20 import java.util.Comparator ; 21 import java.util.List ; 22 import java.util.StringTokenizer ; 23 24 33 public final class DirectoryBrowserSupport { 34 35 public final ModelObject owner; 36 37 public DirectoryBrowserSupport(ModelObject owner) { 38 this.owner = owner; 39 } 40 41 50 public final void serveFile(StaplerRequest req, StaplerResponse rsp, FilePath root, String icon, boolean serveDirIndex) throws IOException , ServletException , InterruptedException { 51 if(req.getQueryString()!=null) { 52 req.setCharacterEncoding("UTF-8"); 53 String path = req.getParameter("path"); 54 if(path!=null) { 55 rsp.sendRedirect(URLEncoder.encode(path,"UTF-8")); 56 return; 57 } 58 } 59 60 String path = req.getRestOfPath(); 61 62 if(path.length()==0) 63 path = "/"; 64 65 if(path.indexOf("..")!=-1 || path.length()<1) { 66 rsp.sendError(HttpServletResponse.SC_BAD_REQUEST); 68 return; 69 } 70 71 FilePath f = new FilePath(root,path.substring(1)); 72 73 boolean isFingerprint=false; 74 if(f.getName().equals("*fingerprint*")) { 75 f = f.getParent(); 76 isFingerprint = true; 77 } 78 79 if(f.isDirectory()) { 80 if(!req.getRequestURL().toString().endsWith("/")) { 81 rsp.sendRedirect2(req.getRequestURL().append('/').toString()); 82 return; 83 } 84 85 if(serveDirIndex) { 86 req.setAttribute("it",this); 87 List <Path> parentPaths = buildParentPath(path); 88 req.setAttribute("parentPath",parentPaths); 89 req.setAttribute("topPath", 90 parentPaths.isEmpty() ? "." : repeat("../",parentPaths.size())); 91 req.setAttribute("files", f.act(new ChildPathBuilder())); 92 req.setAttribute("icon",icon); 93 req.setAttribute("path",path); 94 req.getView(this,"dir.jelly").forward(req,rsp); 95 return; 96 } else { 97 f = f.child("index.html"); 98 } 99 } 100 101 if(!f.exists()) { 102 rsp.sendError(HttpServletResponse.SC_NOT_FOUND); 103 return; 104 } 105 106 if(isFingerprint) { 107 rsp.forward(Hudson.getInstance().getFingerprint(f.digest()),"/",req); 108 } else { 109 ContentInfo ci = f.act(new ContentInfo()); 110 111 InputStream in = f.read(); 112 rsp.serveFile(req, in, ci.lastModified, -1, ci.contentLength, f.getName() ); 113 in.close(); 114 } 115 } 116 117 private static final class ContentInfo implements FileCallable<ContentInfo> { 118 int contentLength; 119 long lastModified; 120 121 public ContentInfo invoke(File f, VirtualChannel channel) throws IOException { 122 contentLength = (int) f.length(); 123 lastModified = f.lastModified(); 124 return this; 125 } 126 127 private static final long serialVersionUID = 1L; 128 } 129 130 134 private List <Path> buildParentPath(String pathList) { 135 List <Path> r = new ArrayList <Path>(); 136 StringTokenizer tokens = new StringTokenizer (pathList, "/"); 137 int total = tokens.countTokens(); 138 int current=1; 139 while(tokens.hasMoreTokens()) { 140 String token = tokens.nextToken(); 141 r.add(new Path(repeat("../",total-current),token,true,0)); 142 current++; 143 } 144 return r; 145 } 146 147 private static String repeat(String s,int times) { 148 StringBuffer buf = new StringBuffer (s.length()*times); 149 for(int i=0; i<times; i++ ) 150 buf.append(s); 151 return buf.toString(); 152 } 153 154 157 public static final class Path implements Serializable { 158 161 private final String href; 162 165 private final String title; 166 167 private final boolean isFolder; 168 169 172 private final long size; 173 174 public Path(String href, String title, boolean isFolder, long size) { 175 this.href = href; 176 this.title = title; 177 this.isFolder = isFolder; 178 this.size = size; 179 } 180 181 public boolean isFolder() { 182 return isFolder; 183 } 184 185 public String getHref() { 186 return href; 187 } 188 189 public String getTitle() { 190 return title; 191 } 192 193 public String getIconName() { 194 return isFolder?"folder.gif":"text.gif"; 195 } 196 197 public long getSize() { 198 return size; 199 } 200 201 private static final long serialVersionUID = 1L; 202 } 203 204 205 206 private static final class FileComparator implements Comparator <File > { 207 public int compare(File lhs, File rhs) { 208 int r = dirRank(lhs)-dirRank(rhs); 210 if(r!=0) return r; 211 return lhs.getName().compareTo(rhs.getName()); 213 } 214 215 private int dirRank(File f) { 216 if(f.isDirectory()) return 0; 217 else return 1; 218 } 219 } 220 221 226 private static final class ChildPathBuilder implements FileCallable<List <List <Path>>> { 227 public List <List <Path>> invoke(File cur, VirtualChannel channel) throws IOException { 228 List <List <Path>> r = new ArrayList <List <Path>>(); 229 230 File [] files = cur.listFiles(); 231 Arrays.sort(files,new FileComparator()); 232 233 for( File f : files ) { 234 Path p = new Path(f.getName(),f.getName(),f.isDirectory(),f.length()); 235 if(!f.isDirectory()) { 236 r.add(Collections.singletonList(p)); 237 } else { 238 List <Path> l = new ArrayList <Path>(); 240 l.add(p); 241 String relPath = f.getName(); 242 while(true) { 243 File [] sub = f.listFiles(new FilenameFilter () { 245 public boolean accept(File dir, String name) { 246 return !name.startsWith(".") && !name.equals("CVS") && !name.equals(".svn"); 247 } 248 }); 249 if(sub.length!=1 || !sub[0].isDirectory()) 250 break; 251 f = sub[0]; 252 relPath += '/'+f.getName(); 253 l.add(new Path(relPath,f.getName(),true,0)); 254 } 255 r.add(l); 256 } 257 } 258 259 return r; 260 } 261 262 private static final long serialVersionUID = 1L; 263 } 264 } 265 | Popular Tags |