/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2003, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package net.sourceforge.cruisecontrol.servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.math.BigDecimal; import java.util.Comparator; import java.util.StringTokenizer; import java.util.List; import java.util.Collections; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; public class FileServlet extends HttpServlet { private File rootDir; private List<String> indexFiles; public File getRootDir() { return rootDir; } public void init(final ServletConfig servletconfig) throws ServletException { super.init(servletconfig); rootDir = getRootDir(servletconfig); indexFiles = getIndexFiles(servletconfig); } protected File getRootDir(final ServletConfig servletconfig) throws ServletException { final String root = servletconfig.getInitParameter("rootDir"); File rootDirectory = getDirectoryFromName(root); if (rootDirectory == null) { rootDirectory = getLogDir(servletconfig); if (rootDirectory == null) { final String message = "ArtifactServlet not configured correctly in web.xml.\n" + "Either rootDir or logDir must point to existing directory.\n" + "rootDir is currently set to <" + root + "> " + "while logDir is <" + getLogDirParameter(servletconfig) + ">"; throw new ServletException(message); } } return rootDirectory; } protected String getLogDirParameter(final ServletConfig servletconfig) throws ServletException { final ServletContext context = servletconfig.getServletContext(); return context.getInitParameter("logDir"); } protected File getLogDir(final ServletConfig servletconfig) throws ServletException { final String logDir = getLogDirParameter(servletconfig); return getDirectoryFromName(logDir); } List<String> getIndexFiles(final ServletConfig servletconfig) { final ServletContext context = servletconfig.getServletContext(); final String logDir = context.getInitParameter("fileServlet.welcomeFiles"); List<String> indexes = Collections.emptyList(); if (logDir != null) { final StringTokenizer tokenizer = new StringTokenizer(logDir); indexes = new ArrayList<String>(); while (tokenizer.hasMoreTokens()) { final String indexFile = ((String) tokenizer.nextElement()); // note: I am pretty sure there's a known issue with StringTokenizer returning "" (cf ant) // but am offline right now and cannot check. if (!"".equals(indexFile)) { indexes.add(indexFile); } } } return indexes; } private static File getDirectoryFromName(final String dir) { if (dir == null) { return null; } final File rootDirectory = new File(dir); if (!rootDirectory.exists() || rootDirectory.isFile()) { return null; } return rootDirectory; } public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { WebFile file = getSubWebFile(request.getPathInfo()); if (file.isDir()) { // note we might want to append the queryString just in case... if (!request.getPathInfo().endsWith("/")) { response.sendRedirect(response.encodeRedirectURL(request.getRequestURI() + '/')); return; } final String index = getIndexFile(file); if (index != null) { file = getSubWebFile(request.getPathInfo() + index); } } if (file.isFile()) { final String filename = file.getName(); final String mimeType; if (request.getParameter("mimetype") != null) { mimeType = request.getParameter("mimetype"); } else { mimeType = getMimeType(filename); } final Date date = new Date(file.getFile().lastModified()); response.addDateHeader("Last-Modified", date.getTime()); response.setContentType(mimeType); response.setContentLength((int) file.getFile().length()); file.write(response.getOutputStream()); return; } response.setContentType("text/html"); final Writer writer = response.getWriter(); writer.write("<html>"); writer.write("<body>"); writer.write("<h1>" + file + "</h1>"); if (file.isDir()) { printDirs(request, file, writer); } else { response.setStatus(HttpServletResponse.SC_NOT_FOUND); writer.write("<h1>Invalid File or Directory</h1>"); } writer.write("</body>"); writer.write("</html>"); } protected String getMimeType(final String filename) { String mimeType = getServletContext().getMimeType(filename); if (mimeType == null) { mimeType = getDefaultMimeType(); } return mimeType; } protected String getDefaultMimeType() { return "text/plain"; } /** * @param dir the directory in which to search. * @return the name of the first found known index file under the * specified directory or <code>null</code> if none found * @throws IllegalArgumentException if the specified WebFile is not a directory */ private String getIndexFile(final WebFile dir) { if (!dir.isDir()) { throw new IllegalArgumentException(dir + " is not a directory"); } for (final String indexFile : indexFiles) { final File file = new File(dir.getFile(), indexFile); // what about hidden files? let's display them... if (file.exists() && file.isFile()) { return indexFile; } } return null; } /** * Returns an HTML snippet that allows the browsing of the directory's content. * * @param request incoming http request * @param file directory to browse * @param writer place to write html output * @throws IOException if an error occurs */ void printDirs(final HttpServletRequest request, final WebFile file, final Writer writer) throws IOException { final File[] files = file.list(); writer.write("<table border='black' borderwidth='1' cellpadding='5'>"); writer.write("<tr><th>name</th><th>file size</th><th>modified date</th></tr>"); for (final File currentFile : files) { final String requestURI = request.getRequestURI(); final int jsessionidIdx = requestURI.indexOf(";jsessionid"); final String shortRequestURI; final String jsessionid; if (jsessionidIdx >= 0) { shortRequestURI = requestURI.substring(0, jsessionidIdx); jsessionid = requestURI.substring(jsessionidIdx); } else { shortRequestURI = requestURI; jsessionid = ""; } final String subFilePath = request.getPathInfo() + '/' + currentFile.getName(); final WebFile sub = getSubWebFile(subFilePath); writer.write("\n<tr><td>"); writer.write( "<a href=\"" + shortRequestURI + (shortRequestURI.endsWith("/") ? "" : "/") + currentFile.getName() + jsessionid + "\">" + currentFile.getName() + (sub.isDir() ? "/" : "") + "</a>"); writer.write("</td><td align='right'>"); writer.write(formatFileSize(currentFile.length())); writer.write("</td><td align='right'>"); writer.write(formatFileDate(currentFile.lastModified())); writer.write("</td></tr>"); } writer.write("</table>"); } private String formatFileDate(final long date) { return new Date(date).toString(); } private static final BigDecimal TEN = BigDecimal.valueOf(10); private static final BigDecimal KB = BigDecimal.valueOf(1024); private static final BigDecimal MB = BigDecimal.valueOf(1024 * 1024); private static final BigDecimal GB = BigDecimal.valueOf(1024 * 1024 * 1024); private String formatFileSize(final long argL) { if (argL < 1024) { return String.valueOf(argL); } if (argL < 1024 * 1024) { return BigDecimal.valueOf(argL).multiply(TEN).divideToIntegralValue(KB).divide(TEN) + "K"; } if (argL < 1024 * 1024 * 1024) { return BigDecimal.valueOf(argL).multiply(TEN).divideToIntegralValue(MB).divide(TEN) + "M"; } return BigDecimal.valueOf(argL).multiply(TEN).divideToIntegralValue(GB).divide(TEN) + "G"; } protected WebFile getSubWebFile(final String subFilePath) { return new WebFile(rootDir, subFilePath); } } class WebFile { private final File file; public WebFile(File logfile) { file = logfile; } public WebFile(File root, String path) { file = WebFile.parsePath(root, path); } public String getName() { return file.getName(); } public boolean isDir() { return file.isDirectory(); } protected InputStream getInputStream() throws IOException { return new FileInputStream(file); } public void write(ServletOutputStream stream) throws IOException { InputStream input = new BufferedInputStream(getInputStream()); OutputStream output = new BufferedOutputStream(stream); try { int i; while ((i = input.read()) != -1) { output.write(i); } } finally { input.close(); output.flush(); } } public boolean isFile() { return file.isFile(); } private static File parsePath(File rootDir, String string) { if (string == null || string.trim().length() == 0 || string.equals("/")) { return rootDir; } String filename = string.replace('/', File.separatorChar); filename = filename.replace('\\', File.separatorChar); return new File(rootDir, filename); } public File[] list() { File[] files = file.listFiles(); if (files == null) { files = new File[0]; } else { Arrays.sort(files, new FileNameComparator()); } return files; } public String toString() { return file.toString(); } public File getFile() { return file; } class FileNameComparator implements Comparator<File> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ /** * {@inheritDoc} */ public int compare(final File f1, final File f2) { return f1.getName().compareTo(f2.getName()); } } }