X-Git-Url: https://code.grnet.gr/git/pithos/blobdiff_plain/9fd42cf4c3fcfab7d6fa0bc0d50db628afec0502..d235f94fe83b8722ffb33b1581945dee8d64874e:/src/gr/ebs/gss/server/rest/FilesHandler.java diff --git a/src/gr/ebs/gss/server/rest/FilesHandler.java b/src/gr/ebs/gss/server/rest/FilesHandler.java index ca54d50..0366a90 100644 --- a/src/gr/ebs/gss/server/rest/FilesHandler.java +++ b/src/gr/ebs/gss/server/rest/FilesHandler.java @@ -18,12 +18,14 @@ */ package gr.ebs.gss.server.rest; +import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration; import gr.ebs.gss.client.exceptions.DuplicateNameException; import gr.ebs.gss.client.exceptions.GSSIOException; import gr.ebs.gss.client.exceptions.InsufficientPermissionsException; import gr.ebs.gss.client.exceptions.ObjectNotFoundException; import gr.ebs.gss.client.exceptions.QuotaExceededException; import gr.ebs.gss.client.exceptions.RpcException; +import gr.ebs.gss.server.Login; import gr.ebs.gss.server.domain.FileUploadStatus; import gr.ebs.gss.server.domain.User; import gr.ebs.gss.server.domain.dto.FileBodyDTO; @@ -32,7 +34,9 @@ import gr.ebs.gss.server.domain.dto.FolderDTO; import gr.ebs.gss.server.domain.dto.GroupDTO; import gr.ebs.gss.server.domain.dto.PermissionDTO; import gr.ebs.gss.server.ejb.ExternalAPI; +import gr.ebs.gss.server.ejb.TransactionHelper; import gr.ebs.gss.server.webdav.Range; +import gr.ebs.gss.server.webdav.RequestUtil; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -51,9 +55,11 @@ import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.StringTokenizer; @@ -62,9 +68,11 @@ import java.util.concurrent.Callable; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; @@ -130,12 +138,35 @@ public class FilesHandler extends RequestHandler { private ServletContext context; /** + * The style sheet for displaying the directory listings. + */ + private static final String GSS_CSS = "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} " + "H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} " + "H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} " + "BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " + "B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} " + "P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}" + "A {color : black;}" + "A.name {color : black;}" + "HR {color : #525D76;}"; + + + /** * @param servletContext */ public FilesHandler(ServletContext servletContext) { context = servletContext; } + private void updateAccounting(final User user, final Date date, final long bandwidthDiff) { + try { + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().updateAccounting(user, date, bandwidthDiff); + return null; + } + }); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + // updateAccounting() doesn't throw any checked exceptions + assert false; + } + } + /** * Serve the specified resource, optionally including the data content. * @@ -164,7 +195,7 @@ public class FilesHandler extends RequestHandler { } String progress = req.getParameter(PROGRESS_PARAMETER); - if (logger.isDebugEnabled()) + if (logger.isDebugEnabled()) if (content) logger.debug("Serving resource '" + path + "' headers and data"); else @@ -172,7 +203,6 @@ public class FilesHandler extends RequestHandler { User user = getUser(req); User owner = getOwner(req); - if (user == null) user = owner; boolean exists = true; Object resource = null; FileHeaderDTO file = null; @@ -186,89 +216,135 @@ public class FilesHandler extends RequestHandler { return; } - if (!exists) { - if (authDeferred) { - // We do not want to leak information if the request - // was not authenticated. - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - // A request for upload progress. - if (progress != null && content) { - serveProgress(req, resp, progress, user, null); - return; - } - - resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI()); + if (!exists && authDeferred) { + // We do not want to leak information if the request + // was not authenticated. + resp.sendError(HttpServletResponse.SC_FORBIDDEN); return; } if (resource instanceof FolderDTO) folder = (FolderDTO) resource; else - file = (FileHeaderDTO) resource; + file = (FileHeaderDTO) resource; // Note that file will be null, if (!exists). // Now it's time to perform the deferred authentication check. // Since regular signature checking was already performed, // we need to check the read-all flag or the signature-in-parameters. if (authDeferred) if (file != null && !file.isReadForAll() && content) { + logger.debug("this case refers to a file with no public privileges"); // Check for GET with the signature in the request parameters. String auth = req.getParameter(AUTHORIZATION_PARAMETER); String dateParam = req.getParameter(DATE_PARAMETER); if (auth == null || dateParam == null) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } + // Check for a valid authentication cookie. + if (req.getCookies() != null) { + boolean found = false; + for (Cookie cookie : req.getCookies()) + if (Login.AUTH_COOKIE.equals(cookie.getName())) { + String cookieauth = cookie.getValue(); + int sepIndex = cookieauth.indexOf(Login.COOKIE_SEPARATOR); + if (sepIndex == -1) { + handleAuthFailure(req, resp); + return; + } + String username = URLDecoder.decode(cookieauth.substring(0, sepIndex), "US-ASCII"); + user = null; + try { + user = getService().findUser(username); + } catch (RpcException e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + return; + } + if (user == null) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + req.setAttribute(USER_ATTRIBUTE, user); + String token = cookieauth.substring(sepIndex + 1); + if (user.getAuthToken() == null) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + if (!Arrays.equals(user.getAuthToken(), Base64.decodeBase64(token))) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + found = true; + break; + } + if (!found) { + handleAuthFailure(req, resp); + return; + } + } else { + handleAuthFailure(req, resp); + return; + } + } else { + long timestamp; + try { + timestamp = DateUtil.parseDate(dateParam).getTime(); + } catch (DateParseException e) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); + return; + } + if (!isTimeValid(timestamp)) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } - long timestamp; - try { - timestamp = DateUtil.parseDate(dateParam).getTime(); - } catch (DateParseException e) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); - return; - } - if (!isTimeValid(timestamp)) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - // Fetch the Authorization parameter and find the user specified in it. - String[] authParts = auth.split(" "); - if (authParts.length != 2) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - String username = authParts[0]; - String signature = authParts[1]; - user = null; - try { - user = getService().findUser(username); - } catch (RpcException e) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); - return; + // Fetch the Authorization parameter and find the user specified in it. + String[] authParts = auth.split(" "); + if (authParts.length != 2) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + String username = authParts[0]; + String signature = authParts[1]; + user = null; + try { + user = getService().findUser(username); + } catch (RpcException e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + return; + } + if (user == null) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + req.setAttribute(USER_ATTRIBUTE, user); + + // Remove the servlet path from the request URI. + String p = req.getRequestURI(); + String servletPath = req.getContextPath() + req.getServletPath(); + p = p.substring(servletPath.length()); + // Validate the signature in the Authorization parameter. + String data = req.getMethod() + dateParam + p; + if (!isSignatureValid(signature, user, data)) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } } - if (user == null) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - req.setAttribute(USER_ATTRIBUTE, user); - - // Remove the servlet path from the request URI. - String p = req.getRequestURI(); - String servletPath = req.getContextPath() + req.getServletPath(); - p = p.substring(servletPath.length()); - // Validate the signature in the Authorization parameter. - String data = req.getMethod() + dateParam + p; - if (!isSignatureValid(signature, user, data)) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - } else if (file != null && !file.isReadForAll() || file == null) { - // Check for a read-for-all file request. - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; } + else if(folder != null && folder.isReadForAll() || file != null && file.isReadForAll()){ + //This case refers to a folder or file with public privileges + //For a read-for-all folder request, pretend the owner is making it. + logger.debug("*********this case refers to a folder or file with public privileges"); + user = owner; + req.setAttribute(USER_ATTRIBUTE, user); + }else if(folder != null && !folder.isReadForAll()){ + //this case refers to a folder with no public privileges + logger.debug("*********this case refers to a folder with no public privileges"); + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + else{ + logger.debug("*********ANY other case"); + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } // If the resource is not a collection, and the resource path // ends with "/" or "\", return NOT FOUND. @@ -285,8 +361,8 @@ public class FilesHandler extends RequestHandler { // A request for upload progress. if (progress != null && content) { if (file == null) { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; + resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI()); + return; } serveProgress(req, resp, progress, user, file); return; @@ -321,21 +397,46 @@ public class FilesHandler extends RequestHandler { // satisfied. Doing this for folders would require recursive checking // for all of their children, which in turn would defy the purpose of // the optimization. - if (folder == null) + if (folder == null){ // Checking If headers. if (!checkIfHeaders(req, resp, file, oldBody)) return; + } + else if(!checkIfModifiedSince(req, resp, folder)) + return; // Find content type. String contentType = null; + boolean isContentHtml = false; + boolean expectJSON = false; + if (file != null) { contentType = version>0 ? oldBody.getMimeType() : file.getMimeType(); if (contentType == null) { contentType = context.getMimeType(file.getName()); file.setMimeType(contentType); } - } else - contentType = "application/json;charset=UTF-8"; + } else { // folder != null + String accept = req.getHeader("Accept"); + // The order in this conditional pessimizes the common API case, + // but is important for backwards compatibility with existing + // clients who send no accept header and expect a JSON response. + if (accept != null && accept.contains("text/html")) { + contentType = "text/html;charset=UTF-8"; + isContentHtml = true; + }else if (accept != null && accept.contains("text/html") && !authDeferred){ + //this is the case when clients send the appropriate headers, the contentType is "text/html" + //and expect a JSON response. The above check applies to FireGSS client + contentType = "text/html;charset=UTF-8"; + isContentHtml = true; + expectJSON = true; + } + else{ + contentType = "application/json;charset=UTF-8"; + expectJSON = true; + } + } + ArrayList ranges = null; long contentLength = -1L; @@ -388,11 +489,7 @@ public class FilesHandler extends RequestHandler { else throw e; } - - if (folder != null - || (ranges == null || ranges.isEmpty()) - && req.getHeader("Range") == null - || ranges == FULL) { + if (folder != null || (ranges == null || ranges.isEmpty()) && req.getHeader("Range") == null || ranges == FULL) { // Set the appropriate output headers if (contentType != null) { if (logger.isDebugEnabled()) @@ -404,21 +501,31 @@ public class FilesHandler extends RequestHandler { logger.debug("contentLength=" + contentLength); if (contentLength < Integer.MAX_VALUE) resp.setContentLength((int) contentLength); + else // Set the content-length as String to be able to use a long resp.setHeader("content-length", "" + contentLength); } InputStream renderResult = null; - if (folder != null) - if (content) - // Serve the directory browser + String relativePath = getRelativePath(req); + String contextPath = req.getContextPath(); + String servletPath = req.getServletPath(); + String contextServletPath = contextPath + servletPath; + if (folder != null && content) + // Serve the directory browser for a public folder + if (isContentHtml && !expectJSON) + renderResult = renderHtml(contextServletPath, relativePath, folder,user); + // Serve the directory for an ordinary folder or for fireGSS client + else try { - renderResult = renderJson(user, folder); - } catch (InsufficientPermissionsException e) { - resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); - return; - } + renderResult = renderJson(user, folder); + } catch (InsufficientPermissionsException e) { + resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + return; + } + + // Copy the input stream to our output stream (if requested) if (content) { try { @@ -429,14 +536,14 @@ public class FilesHandler extends RequestHandler { try { if(file != null) if (needsContentDisposition(req)) - resp.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+URLEncoder.encode(file.getName(),"UTF-8")); + resp.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+getDispositionFilename(file)); else - resp.setHeader("Content-Disposition","inline; filename*=UTF-8''"+URLEncoder.encode(file.getName(),"UTF-8")); + resp.setHeader("Content-Disposition","inline; filename*=UTF-8''"+getDispositionFilename(file)); if (ostream != null) copy(file, renderResult, ostream, req, oldBody); else copy(file, renderResult, writer, req, oldBody); - if (file!=null) getService().updateAccounting(owner, new Date(), contentLength); + if (file!=null) updateAccounting(owner, new Date(), contentLength); } catch (ObjectNotFoundException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return; @@ -446,7 +553,7 @@ public class FilesHandler extends RequestHandler { } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; - } + } } } else { if (ranges == null || ranges.isEmpty()) @@ -484,7 +591,7 @@ public class FilesHandler extends RequestHandler { copy(file, ostream, range, req, oldBody); else copy(file, writer, range, req, oldBody); - getService().updateAccounting(owner, new Date(), contentLength); + updateAccounting(owner, new Date(), contentLength); } catch (ObjectNotFoundException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return; @@ -509,7 +616,7 @@ public class FilesHandler extends RequestHandler { copy(file, ostream, ranges.iterator(), contentType, req, oldBody); else copy(file, writer, ranges.iterator(), contentType, req, oldBody); - getService().updateAccounting(owner, new Date(), contentLength); + updateAccounting(owner, new Date(), contentLength); } catch (ObjectNotFoundException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return; @@ -526,6 +633,33 @@ public class FilesHandler extends RequestHandler { } /** + * Handles an authentication failure. If no Authorization or Date request + * parameters and no Authorization, Date or X-GSS-Date headers were present, + * this is a browser request, so redirect to login and then let the user get + * back to the file. Otherwise it's a bogus client request and Forbidden is + * returned. + */ + private void handleAuthFailure(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (req.getParameter(AUTHORIZATION_PARAMETER) == null && + req.getParameter(DATE_PARAMETER) == null && + req.getHeader(AUTHORIZATION_HEADER) == null && + req.getDateHeader(DATE_HEADER) == -1 && + req.getDateHeader(GSS_DATE_HEADER) == -1) + resp.sendRedirect(getConfiguration().getString("loginUrl") + + "?next=" + req.getRequestURL().toString()); + else + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + } + + /** + * Return the filename of the specified file properly formatted for + * including in the Content-Disposition header. + */ + private String getDispositionFilename(FileHeaderDTO file) throws UnsupportedEncodingException { + return URLEncoder.encode(file.getName(),"UTF-8").replaceAll("\\+", "%20"); + } + + /** * Determines whether the user agent needs the Content-Disposition * header to be set, in order to properly download a file. * @@ -611,6 +745,10 @@ public class FilesHandler extends RequestHandler { } String newName = req.getParameter(NEW_FOLDER_PARAMETER); + if (!isValidResourceName(newName)) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER); boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER); boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER); @@ -647,7 +785,7 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an I/O error occurs */ private void restoreVersion(HttpServletRequest req, HttpServletResponse resp, String path, String version) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; try { @@ -665,9 +803,16 @@ public class FilesHandler extends RequestHandler { } try { - FileHeaderDTO file = (FileHeaderDTO) resource; - int oldVersion = Integer.parseInt(version); - getService().restoreVersion(user.getId(), file.getId(), oldVersion); + final FileHeaderDTO file = (FileHeaderDTO) resource; + final int oldVersion = Integer.parseInt(version); + + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().restoreVersion(user.getId(), file.getId(), oldVersion); + return null; + } + }); } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } catch (ObjectNotFoundException e) { @@ -680,6 +825,8 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage()); } catch (NumberFormatException e) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } @@ -722,7 +869,6 @@ public class FilesHandler extends RequestHandler { return; } - FolderDTO folder = null; Object parent; String parentPath = null; try { @@ -739,8 +885,8 @@ public class FilesHandler extends RequestHandler { response.sendError(HttpServletResponse.SC_CONFLICT); return; } - folder = (FolderDTO) parent; - String fileName = getLastElement(path); + final FolderDTO folder = (FolderDTO) parent; + final String fileName = getLastElement(path); FileItemIterator iter; File uploadedFile = null; @@ -822,7 +968,7 @@ public class FilesHandler extends RequestHandler { progressListener.setUserId(user.getId()); progressListener.setFilename(fileName); - String contentType = item.getContentType(); + final String contentType = item.getContentType(); try { uploadedFile = getService().uploadFile(stream, user.getId()); @@ -830,11 +976,24 @@ public class FilesHandler extends RequestHandler { throw new GSSIOException(ex, false); } FileHeaderDTO fileDTO = null; + final File upf = uploadedFile; + final FileHeaderDTO f = file; + final User u = user; if (file == null) - fileDTO = getService().createFile(user.getId(), folder.getId(), fileName, contentType, uploadedFile.getCanonicalFile().length(), uploadedFile.getAbsolutePath()); + fileDTO = new TransactionHelper().tryExecute(new Callable() { + @Override + public FileHeaderDTO call() throws Exception { + return getService().createFile(u.getId(), folder.getId(), fileName, contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath()); + } + }); else - fileDTO = getService().updateFileContents(user.getId(), file.getId(), contentType, uploadedFile.getCanonicalFile().length(), uploadedFile.getAbsolutePath()); - getService().updateAccounting(owner, new Date(), fileDTO.getFileSize()); + fileDTO = new TransactionHelper().tryExecute(new Callable() { + @Override + public FileHeaderDTO call() throws Exception { + return getService().updateFileContents(u.getId(), f.getId(), contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath()); + } + }); + updateAccounting(owner, new Date(), fileDTO.getFileSize()); getService().removeFileUploadProgress(user.getId(), fileName); } } @@ -888,44 +1047,15 @@ public class FilesHandler extends RequestHandler { String error = "An error occurred while communicating with the service"; logger.error(error, e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error); + } catch (Exception e) { + if (uploadedFile != null && uploadedFile.exists()) + uploadedFile.delete(); + String error = "An internal server error occurred"; + logger.error(error, e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error); } } - private String getBackupFilename(FileHeaderDTO file, String filename){ - List deletedFiles = new ArrayList(); - try{ - deletedFiles = getService().getDeletedFiles(file.getOwner().getId()); - } - catch(ObjectNotFoundException e){ - - } catch (RpcException e) { - - } - List filesInSameFolder = new ArrayList(); - for(FileHeaderDTO deleted : deletedFiles) - if(deleted.getFolder().getId().equals(file.getFolder().getId())) - filesInSameFolder.add(deleted); - int i=1; - String filenameToCheck = filename; - for(FileHeaderDTO same : filesInSameFolder) - if(same.getName().startsWith(filename)){ - String toCheck=same.getName().substring(filename.length(),same.getName().length()); - if(toCheck.startsWith(" ")){ - int test =-1; - try{ - test = Integer.valueOf(toCheck.replace(" ","")); - } - catch(NumberFormatException e){ - //do nothing since string is not a number - } - if(test>=i) - i = test+1; - } - } - - return filename+" "+i; - } - /** * Move the resource in the specified path to the specified destination. * @@ -936,7 +1066,7 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an input/output error occurs */ private void moveResource(HttpServletRequest req, HttpServletResponse resp, String path, String moveTo) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; try { @@ -972,12 +1102,27 @@ public class FilesHandler extends RequestHandler { } try { + final User dOwner = destOwner; + final String dest = destination; if (resource instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) resource; - getService().moveFolderToPath(user.getId(), destOwner.getId(), folder.getId(), destination); + final FolderDTO folder = (FolderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().moveFolderToPath(user.getId(), dOwner.getId(), folder.getId(), dest); + return null; + } + }); } else { - FileHeaderDTO file = (FileHeaderDTO) resource; - getService().moveFileToPath(user.getId(), destOwner.getId(), file.getId(), destination); + final FileHeaderDTO file = (FileHeaderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().moveFileToPath(user.getId(), dOwner.getId(), file.getId(), dest); + return null; + } + }); + } } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); @@ -991,6 +1136,8 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } catch (QuotaExceededException e) { resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage()); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination); } } @@ -1004,7 +1151,7 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an input/output error occurs */ private void copyResource(HttpServletRequest req, HttpServletResponse resp, String path, String copyTo) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; try { @@ -1040,12 +1187,26 @@ public class FilesHandler extends RequestHandler { } try { + final User dOwner = destOwner; + final String dest = destination; if (resource instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) resource; - getService().copyFolderStructureToPath(user.getId(), destOwner.getId(), folder.getId(), destination); + final FolderDTO folder = (FolderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().copyFolderStructureToPath(user.getId(), dOwner.getId(), folder.getId(), dest); + return null; + } + }); } else { - FileHeaderDTO file = (FileHeaderDTO) resource; - getService().copyFileToPath(user.getId(), destOwner.getId(), file.getId(), destination); + final FileHeaderDTO file = (FileHeaderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().copyFileToPath(user.getId(), dOwner.getId(), file.getId(), dest); + return null; + } + }); } } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); @@ -1059,6 +1220,8 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } catch (QuotaExceededException e) { resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage()); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination); } } @@ -1131,7 +1294,7 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an input/output error occurs */ private void trashResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; try { @@ -1146,11 +1309,23 @@ public class FilesHandler extends RequestHandler { try { if (resource instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) resource; - getService().moveFolderToTrash(user.getId(), folder.getId()); + final FolderDTO folder = (FolderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().moveFolderToTrash(user.getId(), folder.getId()); + return null; + } + }); } else { - FileHeaderDTO file = (FileHeaderDTO) resource; - getService().moveFileToTrash(user.getId(), file.getId()); + final FileHeaderDTO file = (FileHeaderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().moveFileToTrash(user.getId(), file.getId()); + return null; + } + }); } } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); @@ -1158,6 +1333,8 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); } } @@ -1170,7 +1347,7 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an input/output error occurs */ private void restoreResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; try { @@ -1185,11 +1362,23 @@ public class FilesHandler extends RequestHandler { try { if (resource instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) resource; - getService().removeFolderFromTrash(user.getId(), folder.getId()); + final FolderDTO folder = (FolderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().removeFolderFromTrash(user.getId(), folder.getId()); + return null; + } + }); } else { - FileHeaderDTO file = (FileHeaderDTO) resource; - getService().removeFileFromTrash(user.getId(), file.getId()); + final FileHeaderDTO file = (FileHeaderDTO) resource; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().removeFileFromTrash(user.getId(), file.getId()); + return null; + } + }); } } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); @@ -1197,6 +1386,8 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); } } @@ -1209,9 +1400,10 @@ public class FilesHandler extends RequestHandler { * @throws IOException if an input/output error occurs */ private void updateResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException { - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); Object resource = null; + try { resource = getService().getResourceAtPath(owner.getId(), path, false); } catch (ObjectNotFoundException e) { @@ -1221,49 +1413,56 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); return; } - //use utf-8 encoding for reading request - BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8")); StringBuffer input = new StringBuffer(); - String line = null; JSONObject json = null; - while ((line = reader.readLine()) != null) - input.append(line); - reader.close(); + if (req.getContentType() != null && req.getContentType().startsWith("application/x-www-form-urlencoded")) + input.append(req.getParameter(RESOURCE_UPDATE_PARAMETER)); + else { + // Assume application/json + BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8")); + String line = null; + while ((line = reader.readLine()) != null) + input.append(line); + reader.close(); + } try { json = new JSONObject(input.toString()); if (logger.isDebugEnabled()) logger.debug("JSON update: " + json); if (resource instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) resource; + final FolderDTO folder = (FolderDTO) resource; String name = json.optString("name"); - if (!name.isEmpty()){ - try { - name = URLDecoder.decode(name, "UTF-8"); - } catch (IllegalArgumentException e) { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); - return; - } - getService().modifyFolder(user.getId(), folder.getId(), name); - FolderDTO folderUpdated = getService().getFolder(user.getId(), folder.getId()); - String parentUrl =URLDecoder.decode(getContextPath(req, true),"UTF-8"); - String fpath = URLDecoder.decode(req.getPathInfo(), "UTF-8"); - parentUrl = parentUrl.replaceAll(fpath, ""); - if(!parentUrl.endsWith("/")) - parentUrl = parentUrl+"/"; - parentUrl = parentUrl+folderUpdated.getOwner().getUsername()+PATH_FILES+folderUpdated.getPath(); - resp.getWriter().println(parentUrl); - } - JSONArray permissions = json.optJSONArray("permissions"); - if (permissions != null) { - Set perms = parsePermissions(user, permissions); - getService().setFolderPermissions(user.getId(), folder.getId(), perms); + Set perms = null; + if (permissions != null) + perms = parsePermissions(user, permissions); + Boolean readForAll = null; + if (json.opt("readForAll") != null) + readForAll = json.optBoolean("readForAll"); + if (!name.isEmpty() || permissions != null || readForAll != null) { + final String fName = name.isEmpty()? null: name; + final Boolean freadForAll = readForAll; + final Set fPerms = perms; + FolderDTO folderUpdated = new TransactionHelper().tryExecute(new Callable() { + @Override + public FolderDTO call() throws Exception { + return getService().updateFolder(user.getId(), folder.getId(), fName, freadForAll, fPerms); + } + + }); + resp.getWriter().println(getNewUrl(req, folderUpdated)); } } else { - FileHeaderDTO file = (FileHeaderDTO) resource; + final FileHeaderDTO file = (FileHeaderDTO) resource; String name = null; if (json.opt("name") != null) name = json.optString("name"); + Long modificationDate = null; + if (json.optLong("modificationDate") != 0) + modificationDate = json.optLong("modificationDate"); + Boolean versioned = null; + if (json.opt("versioned") != null) + versioned = json.getBoolean("versioned"); JSONArray tagset = json.optJSONArray("tags"); String tags = null; StringBuffer t = new StringBuffer(); @@ -1272,9 +1471,6 @@ public class FilesHandler extends RequestHandler { t.append(tagset.getString(i) + ','); tags = t.toString(); } - if (name != null || tags != null) - getService().updateFile(user.getId(), file.getId(), name, tags); - JSONArray permissions = json.optJSONArray("permissions"); Set perms = null; if (permissions != null) @@ -1282,12 +1478,25 @@ public class FilesHandler extends RequestHandler { Boolean readForAll = null; if (json.opt("readForAll") != null) readForAll = json.optBoolean("readForAll"); - if (perms != null || readForAll != null) - getService().setFilePermissions(user.getId(), file.getId(), readForAll, perms); - - if (json.opt("versioned") != null) { - boolean versioned = json.getBoolean("versioned"); - getService().toggleFileVersioning(user.getId(), file.getId(), versioned); + if (name != null || tags != null || modificationDate != null + || versioned != null || perms != null + || readForAll != null) { + final String fName = name; + final String fTags = tags; + final Date mDate = modificationDate != null? new Date(modificationDate): null; + final Boolean fVersioned = versioned; + final Boolean fReadForAll = readForAll; + final Set fPerms = perms; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Object call() throws Exception { + getService().updateFile(user.getId(), file.getId(), + fName, fTags, mDate, fVersioned, + fReadForAll, fPerms); + return null; + } + + }); } } } catch (JSONException e) { @@ -1300,10 +1509,27 @@ public class FilesHandler extends RequestHandler { resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage()); } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + return; } } /** + * Returns the new URL of an updated folder. + */ + private String getNewUrl(HttpServletRequest req, FolderDTO folder) throws UnsupportedEncodingException { + String parentUrl = URLDecoder.decode(getContextPath(req, true),"UTF-8"); + String fpath = URLDecoder.decode(getRelativePath(req), "UTF-8"); + if (parentUrl.indexOf(fpath) != -1) + parentUrl = parentUrl.substring(0, parentUrl.indexOf(fpath)); + if(!parentUrl.endsWith("/")) + parentUrl = parentUrl+"/"; + parentUrl = parentUrl+folder.getOwner().getUsername()+PATH_FILES+folder.getPath(); + return parentUrl; + } + + /** * Helper method to convert a JSON array of permissions into a set of * PermissionDTO objects. * @@ -1313,9 +1539,10 @@ public class FilesHandler extends RequestHandler { * @throws JSONException if there was an error parsing the JSON object * @throws RpcException if there was an error communicating with the EJB * @throws ObjectNotFoundException if the user could not be found + * @throws UnsupportedEncodingException */ private Set parsePermissions(User user, JSONArray permissions) - throws JSONException, RpcException, ObjectNotFoundException { + throws JSONException, RpcException, ObjectNotFoundException, UnsupportedEncodingException { if (permissions == null) return null; Set perms = new HashSet(); @@ -1332,13 +1559,24 @@ public class FilesHandler extends RequestHandler { throw new ObjectNotFoundException("User " + permUser + " not found"); perm.setUser(u.getDTO()); } + // 31/8/2009: Add optional groupUri which takes priority if it exists + String permGroupUri = j.optString("groupUri"); String permGroup = j.optString("group"); - if (!permGroup.isEmpty()) { + if (!permGroupUri.isEmpty()) { + String[] names = permGroupUri.split("/"); + String grp = URLDecoder.decode(names[names.length - 1], "UTF-8"); + String usr = URLDecoder.decode(names[names.length - 3], "UTF-8"); + User u = getService().findUser(usr); + if (u == null) + throw new ObjectNotFoundException("User " + permUser + " not found"); + GroupDTO g = getService().getGroup(u.getId(), grp); + perm.setGroup(g); + } + else if (!permGroup.isEmpty()) { GroupDTO g = getService().getGroup(user.getId(), permGroup); perm.setGroup(g); } - if (permUser.isEmpty() && permGroup.isEmpty() || - permUser.isEmpty() && permGroup.isEmpty()) + if (permUser.isEmpty() && permGroupUri.isEmpty() && permGroup.isEmpty()) throw new JSONException("A permission must correspond to either a user or a group"); perms.add(perm); } @@ -1354,11 +1592,11 @@ public class FilesHandler extends RequestHandler { * @param folderName the name of the new folder * @throws IOException if an input/output error occurs */ - private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, String folderName) throws IOException { + private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, final String folderName) throws IOException { if (logger.isDebugEnabled()) logger.debug("Creating folder " + folderName + " in '" + path); - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); boolean exists = true; try { @@ -1389,9 +1627,15 @@ public class FilesHandler extends RequestHandler { } try { if (parent instanceof FolderDTO) { - FolderDTO folder = (FolderDTO) parent; - getService().createFolder(user.getId(), folder.getId(), folderName); - String newResource = getContextPath(req, true) + folderName; + final FolderDTO folder = (FolderDTO) parent; + FolderDTO newFolder = new TransactionHelper().tryExecute(new Callable() { + @Override + public FolderDTO call() throws Exception { + return getService().createFolder(user.getId(), folder.getId(), folderName); + } + + }); + String newResource = getApiRoot() + newFolder.getURI(); resp.setHeader("Location", newResource); resp.setContentType("text/plain"); PrintWriter out = resp.getWriter(); @@ -1412,6 +1656,9 @@ public class FilesHandler extends RequestHandler { } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName); return; + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); + return; } resp.setStatus(HttpServletResponse.SC_CREATED); } @@ -1501,11 +1748,17 @@ public class FilesHandler extends RequestHandler { throw new GSSIOException(ex, false); } FileHeaderDTO fileDTO = null; + final File uploadedf = uploadedFile; + final FolderDTO parentf = folder; + final FileHeaderDTO f = file; if (exists) - fileDTO = getService().updateFileContents(user.getId(), file.getId(), mimeType, uploadedFile.getCanonicalFile().length(), uploadedFile.getAbsolutePath()); - else { - final File uploadedf = uploadedFile; - final FolderDTO parentf = folder; + fileDTO = new TransactionHelper().tryExecute(new Callable() { + @Override + public FileHeaderDTO call() throws Exception { + return getService().updateFileContents(user.getId(), f.getId(), mimeType, uploadedf.getCanonicalFile().length(), uploadedf.getAbsolutePath()); + } + }); + else fileDTO = new TransactionHelper().tryExecute(new Callable() { @Override public FileHeaderDTO call() throws Exception { @@ -1513,8 +1766,7 @@ public class FilesHandler extends RequestHandler { } }); - } - getService().updateAccounting(owner, new Date(), fileDTO.getFileSize()); + updateAccounting(owner, new Date(), fileDTO.getFileSize()); getService().removeFileUploadProgress(user.getId(), fileDTO.getName()); } catch(ObjectNotFoundException e) { result = false; @@ -1562,7 +1814,7 @@ public class FilesHandler extends RequestHandler { if (logger.isDebugEnabled()) logger.debug("Deleting resource '" + path); path = URLDecoder.decode(path, "UTF-8"); - User user = getUser(req); + final User user = getUser(req); User owner = getOwner(req); boolean exists = true; Object object = null; @@ -1589,7 +1841,14 @@ public class FilesHandler extends RequestHandler { if (file != null) try { - getService().deleteFile(user.getId(), file.getId()); + final FileHeaderDTO f = file; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().deleteFile(user.getId(), f.getId()); + return null; + } + }); } catch (InsufficientPermissionsException e) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return; @@ -1601,10 +1860,20 @@ public class FilesHandler extends RequestHandler { } catch (RpcException e) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; } else if (folder != null) try { - getService().deleteFolder(user.getId(), folder.getId()); + final FolderDTO fo = folder; + new TransactionHelper().tryExecute(new Callable() { + @Override + public Void call() throws Exception { + getService().deleteFolder(user.getId(), fo.getId()); + return null; + } + }); } catch (InsufficientPermissionsException e) { resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return; @@ -1614,6 +1883,9 @@ public class FilesHandler extends RequestHandler { } catch (RpcException e) { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; + } catch (Exception e) { + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; } resp.setStatus(HttpServletResponse.SC_NO_CONTENT); return; @@ -1639,7 +1911,9 @@ public class FilesHandler extends RequestHandler { put("owner", folder.getOwner().getUsername()). put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()). put("creationDate", folder.getAuditInfo().getCreationDate().getTime()). - put("deleted", folder.isDeleted()); + put("deleted", folder.isDeleted()). + put("readForAll", folder.isReadForAll()); + if (folder.getAuditInfo().getModifiedBy() != null) json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()). put("modificationDate", folder.getAuditInfo().getModificationDate().getTime()); @@ -1808,8 +2082,11 @@ public class FilesHandler extends RequestHandler { permission.put("read", p.hasRead()).put("write", p.hasWrite()).put("modifyACL", p.hasModifyACL()); if (p.getUser() != null) permission.put("user", p.getUser().getUsername()); - if (p.getGroup() != null) + if (p.getGroup() != null) { + GroupDTO group = p.getGroup(); + permission.put("groupUri", getApiRoot() + group.getOwner().getUsername() + PATH_GROUPS + "/" + URLEncoder.encode(group.getName(),"UTF-8")); permission.put("group", URLEncoder.encode(p.getGroup().getName(),"UTF-8")); + } perms.put(permission); } return perms; @@ -1853,8 +2130,6 @@ public class FilesHandler extends RequestHandler { private long fileSize = -100; - private long tenKBRead = -1; - private Long userId; private String filename; @@ -1883,6 +2158,7 @@ public class FilesHandler extends RequestHandler { filename = aFilename; } + @Override public void update(long bytesRead, long contentLength, int items) { //monitoring per percent of bytes uploaded bytesTransferred = bytesRead; @@ -1903,4 +2179,184 @@ public class FilesHandler extends RequestHandler { } } } + /** + * Return an InputStream to an HTML representation of the contents of this + * directory. + * + * @param contextPath Context path to which our internal paths are relative + * @param relativePath the requested relative path to the resource + * @param folder the specified directory + * @param req the HTTP request + * @return an input stream with the rendered contents + * @throws IOException + * @throws ServletException + */ + private InputStream renderHtml(String contextPath, String relativePath, FolderDTO folder, User user) throws IOException, ServletException { + String name = folder.getName(); + // Prepare a writer to a buffered area + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8"); + PrintWriter writer = new PrintWriter(osWriter); + StringBuffer sb = new StringBuffer(); + // rewriteUrl(contextPath) is expensive. cache result for later reuse + String rewrittenContextPath = rewriteUrl(contextPath); + // Render the page header + sb.append("\r\n"); + sb.append("\r\n"); + sb.append(""); + sb.append("Index of " + name); + sb.append("\r\n"); + sb.append(" "); + sb.append("\r\n"); + sb.append(""); + sb.append("

"); + sb.append("Index of " + name); + + // Render the link to our parent (if required) + String folderPath = folder.getPath(); + int indexFolderPath = relativePath.indexOf(folderPath); + String relativePathNoFolderName = null; + if(indexFolderPath != 0) + relativePathNoFolderName = relativePath.substring(0, indexFolderPath); + else + relativePathNoFolderName = relativePath; + String parentDirectory = folderPath; + //To-do: further search in encoding folder names with special characters + //String rewrittenParentDirectory = rewriteUrl(parentDirectory); + if (parentDirectory.endsWith("/")) + parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1); + int slash = parentDirectory.lastIndexOf('/'); + parentDirectory = parentDirectory.substring(0,slash); + if (slash >= 0) { + sb.append(" - "); + sb.append(""); + sb.append("Up To "); + if (parentDirectory.equals("")) + parentDirectory = "/"; + sb.append(parentDirectory); + sb.append(""); + sb.append(""); + } + + sb.append("

"); + sb.append("
"); + + sb.append("\r\n"); + + // Render the column headings + sb.append("\r\n"); + sb.append("\r\n"); + sb.append("\r\n"); + sb.append("\r\n"); + sb.append(""); + // Render the directory entries within this directory + boolean shade = false; + Iterator iter = folder.getSubfolders().iterator(); + while (iter.hasNext()) { + FolderDTO subf = (FolderDTO) iter.next(); + String resourceName = subf.getName(); + if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF")) + continue; + + sb.append("\r\n"); + shade = !shade; + + sb.append("\r\n"); + + sb.append("\r\n"); + + sb.append("\r\n"); + + sb.append("\r\n"); + } + List files; + try { + files = getService().getFiles(user.getId(), folder.getId(), true); + } catch (ObjectNotFoundException e) { + throw new ServletException(e.getMessage()); + } catch (InsufficientPermissionsException e) { + throw new ServletException(e.getMessage()); + } catch (RpcException e) { + throw new ServletException(e.getMessage()); + } + for (FileHeaderDTO file : files) + //Display only file resources that are marked as public + if(file.isReadForAll()){ + String resourceName = file.getName(); + if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF")) + continue; + + sb.append("\r\n"); + shade = !shade; + + sb.append("\r\n"); + + sb.append("\r\n"); + + sb.append("\r\n"); + + sb.append("\r\n"); + } + + // Render the page footer + sb.append("
"); + sb.append("Name"); + sb.append(""); + sb.append("Size"); + sb.append(""); + sb.append("Last modified"); + sb.append("
  \r\n"); + sb.append(""); + sb.append(RequestUtil.filter(resourceName)); + sb.append("/"); + sb.append(""); + sb.append(" "); + sb.append(""); + sb.append(getLastModifiedHttp(folder.getAuditInfo())); + sb.append("
  \r\n"); + sb.append(""); + sb.append(RequestUtil.filter(resourceName)); + sb.append(""); + sb.append(renderSize(file.getFileSize())); + sb.append(""); + sb.append(getLastModifiedHttp(file.getAuditInfo())); + sb.append("
\r\n"); + + sb.append("
"); + + //sb.append("

").append(getServletContext().getServerInfo()).append("

"); + sb.append("\r\n"); + sb.append("\r\n"); + + // Return an input stream to the underlying bytes + writer.write(sb.toString()); + writer.flush(); + return new ByteArrayInputStream(stream.toByteArray()); + + } }