retry transaction on delete
[pithos] / src / gr / ebs / gss / server / rest / FilesHandler.java
index f11d086..39ac8aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008, 2009 Electronic Business Systems Ltd.
+ * Copyright 2008, 2009, 2010 Electronic Business Systems Ltd.
  *
  * This file is part of GSS.
  *
  */
 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.FileBody;
+import gr.ebs.gss.server.domain.FileHeader;
 import gr.ebs.gss.server.domain.FileUploadStatus;
+import gr.ebs.gss.server.domain.Folder;
+import gr.ebs.gss.server.domain.Group;
+import gr.ebs.gss.server.domain.Permission;
 import gr.ebs.gss.server.domain.User;
-import gr.ebs.gss.server.domain.dto.FileBodyDTO;
-import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
-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,19 +55,24 @@ 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;
+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;
@@ -129,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<Void>().tryExecute(new Callable<Void>() {
+                               @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.
      *
@@ -171,11 +203,10 @@ 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;
-        FolderDTO folder = null;
+        FileHeader file = null;
+        Folder folder = null;
         try {
                resource = getService().getResourceAtPath(owner.getId(), path, false);
         } catch (ObjectNotFoundException e) {
@@ -185,90 +216,131 @@ 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;
+       if (resource instanceof Folder)
+               folder = (Folder) resource;
        else
-               file = (FileHeaderDTO) resource;
+               file = (FileHeader) 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 (authDeferred) {
                        if (file != null && !file.isReadForAll() && content) {
                                // 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;
-                       }
+                       }
+                       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.
+                               user = owner;
                                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.
+                       }else if(folder != null && !folder.isReadForAll()){
                                resp.sendError(HttpServletResponse.SC_FORBIDDEN);
                                return;
                        }
-
+                       else{
+                               resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+                               return;
+                       }
+               }
        // If the resource is not a collection, and the resource path
        // ends with "/" or "\", return NOT FOUND.
        if (folder == null)
@@ -283,10 +355,6 @@ public class FilesHandler extends RequestHandler {
 
        // A request for upload progress.
        if (progress != null && content) {
-               if (file == null) {
-                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                       return;
-               }
                serveProgress(req, resp, progress, user, file);
                        return;
        }
@@ -294,7 +362,7 @@ public class FilesHandler extends RequestHandler {
                // Fetch the version to retrieve, if specified.
                String verStr = req.getParameter(VERSION_PARAM);
                int version = 0;
-               FileBodyDTO oldBody = null;
+               FileBody oldBody = null;
                if (verStr != null && file != null)
                        try {
                                version = Integer.valueOf(verStr);
@@ -320,21 +388,43 @@ 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();
+               contentType = version>0 ? oldBody.getMimeType() : file.getCurrentBody().getMimeType();
                if (contentType == null) {
                        contentType = context.getMimeType(file.getName());
-                       file.setMimeType(contentType);
+                       file.getCurrentBody().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;
+                       //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
+                       expectJSON = !authDeferred ? true : false;
+               }
+               else {
+                       contentType = "application/json;charset=UTF-8";
+                       expectJSON = true;
+               }
+               }
+
 
        ArrayList ranges = null;
        long contentLength = -1L;
@@ -357,7 +447,7 @@ public class FilesHandler extends RequestHandler {
                        return;
                }
                // Get content length.
-               contentLength = version>0 ? oldBody.getFileSize() : file.getFileSize();
+               contentLength = version>0 ? oldBody.getFileSize() : file.getCurrentBody().getFileSize();
                // Special case for zero length files, which would cause a
                // (silent) ISE when setting the output buffer size.
                if (contentLength == 0L)
@@ -387,11 +477,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())
@@ -403,21 +489,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 {
@@ -426,13 +522,16 @@ public class FilesHandler extends RequestHandler {
                                // Silent catch
                        }
                        try {
-                               if(file != null && needsContentDisposition(req))
-                                               resp.setHeader("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
+                               if(file != null)
+                                               if (needsContentDisposition(req))
+                                               resp.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+getDispositionFilename(file));
+                                       else
+                                               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(user, new Date(), contentLength);
+                               if (file!=null) updateAccounting(owner, new Date(), contentLength);
                        } catch (ObjectNotFoundException e) {
                                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                                return;
@@ -442,7 +541,7 @@ public class FilesHandler extends RequestHandler {
                        } catch (RpcException e) {
                                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                                return;
-                       }
+                       }
                }
        } else {
                if (ranges == null || ranges.isEmpty())
@@ -480,7 +579,7 @@ public class FilesHandler extends RequestHandler {
                                                        copy(file, ostream, range, req, oldBody);
                                                else
                                                        copy(file, writer, range, req, oldBody);
-                                       getService().updateAccounting(user, new Date(), contentLength);
+                                       updateAccounting(owner, new Date(), contentLength);
                        } catch (ObjectNotFoundException e) {
                                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                                return;
@@ -505,7 +604,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(user, new Date(), contentLength);
+                                       updateAccounting(owner, new Date(), contentLength);
                        } catch (ObjectNotFoundException e) {
                                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                                return;
@@ -522,6 +621,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(FileHeader 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.
         *
@@ -529,8 +655,11 @@ public class FilesHandler extends RequestHandler {
         * @return true if the Content-Disposition HTTP header must be set
         */
        private boolean needsContentDisposition(HttpServletRequest req) {
-               String agent = req.getHeader("user-agent");
+               /*String agent = req.getHeader("user-agent");
                if (agent != null && agent.contains("MSIE"))
+                       return true;*/
+               String dl = req.getParameter("dl");
+               if ("1".equals(dl))
                        return true;
                return false;
        }
@@ -547,7 +676,7 @@ public class FilesHandler extends RequestHandler {
         * @throws IOException if an I/O error occurs
         */
        private void serveProgress(HttpServletRequest req, HttpServletResponse resp,
-                               String parameter, User user, FileHeaderDTO file)        throws IOException {
+                               String parameter, User user, FileHeader file)   throws IOException {
                String filename = file == null ? parameter : file.getName();
                try {
                        FileUploadStatus status = getService().getFileUploadStatus(user.getId(), filename);
@@ -604,6 +733,7 @@ public class FilesHandler extends RequestHandler {
                }
 
        String newName = req.getParameter(NEW_FOLDER_PARAMETER);
+
        boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER);
        boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER);
        boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER);
@@ -611,8 +741,13 @@ public class FilesHandler extends RequestHandler {
        String moveTo = req.getParameter(RESOURCE_MOVE_PARAMETER);
        String restoreVersion = req.getParameter(RESTORE_VERSION_PARAMETER);
 
-       if (newName != null)
+       if (newName != null){
+               if (!isValidResourceName(newName)) {
+                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                       return;
+               }
                        createFolder(req, resp, path, newName);
+       }
        else if (hasUpdateParam)
                        updateResource(req, resp, path);
                else if (hasTrashParam)
@@ -626,7 +761,8 @@ public class FilesHandler extends RequestHandler {
                else if (restoreVersion != null)
                        restoreVersion(req, resp, path, restoreVersion);
                else
-                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                       // IE with Gears uses POST for multiple uploads.
+                       putResource(req, resp);
        }
 
        /**
@@ -639,7 +775,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 {
@@ -651,15 +787,22 @@ public class FilesHandler extends RequestHandler {
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
                        return;
                }
-               if (resource instanceof FolderDTO) {
+               if (resource instanceof Folder) {
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
                        return;
                }
 
                try {
-                       FileHeaderDTO file = (FileHeaderDTO) resource;
-                       int oldVersion = Integer.parseInt(version);
-                       getService().restoreVersion(user.getId(), file.getId(), oldVersion);
+                       final FileHeader file = (FileHeader) resource;
+                       final int oldVersion = Integer.parseInt(version);
+
+                       new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                               @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) {
@@ -672,6 +815,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());
                }
        }
 
@@ -692,7 +837,7 @@ public class FilesHandler extends RequestHandler {
        User owner = getOwner(request);
        boolean exists = true;
         Object resource = null;
-        FileHeaderDTO file = null;
+        FileHeader file = null;
         try {
                resource = getService().getResourceAtPath(owner.getId(), path, false);
         } catch (ObjectNotFoundException e) {
@@ -703,8 +848,8 @@ public class FilesHandler extends RequestHandler {
                }
 
         if (exists)
-                       if (resource instanceof FileHeaderDTO) {
-                       file = (FileHeaderDTO) resource;
+                       if (resource instanceof FileHeader) {
+                       file = (FileHeader) resource;
                        if (file.isDeleted()) {
                                response.sendError(HttpServletResponse.SC_CONFLICT, file.getName() + " is in the trash");
                        return;
@@ -714,7 +859,6 @@ public class FilesHandler extends RequestHandler {
                        return;
                }
 
-        FolderDTO folder = null;
        Object parent;
        String parentPath = null;
                try {
@@ -727,12 +871,17 @@ public class FilesHandler extends RequestHandler {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
                        return;
                }
-       if (!(parent instanceof FolderDTO)) {
+       if (!(parent instanceof Folder)) {
                response.sendError(HttpServletResponse.SC_CONFLICT);
                return;
        }
-               folder = (FolderDTO) parent;
-       String fileName = getLastElement(path);
+       final Folder folderLocal = (Folder) parent;
+       final String fileName = getLastElement(path);
+
+       if (!isValidResourceName(fileName)) {
+               response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+               return;
+       }
 
                FileItemIterator iter;
                File uploadedFile = null;
@@ -814,19 +963,32 @@ 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());
                                        } catch (IOException ex) {
                                                throw new GSSIOException(ex, false);
                                        }
-                                       FileHeaderDTO fileDTO = null;
+                                       FileHeader fileLocal = null;
+                                       final File upf = uploadedFile;
+                                       final FileHeader f = file;
+                                       final User u = user;
                                        if (file == null)
-                                               fileDTO = getService().createFile(user.getId(), folder.getId(), fileName, contentType, uploadedFile);
+                                               fileLocal = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() {
+                                                       @Override
+                                                       public FileHeader call() throws Exception {
+                                                               return getService().createFile(u.getId(), folderLocal.getId(), fileName, contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath());
+                                                       }
+                                               });
                                        else
-                                               fileDTO = getService().updateFileContents(user.getId(), file.getId(), contentType, uploadedFile);
-                                       getService().updateAccounting(user, new Date(), fileDTO.getFileSize());
+                                               fileLocal = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() {
+                                                       @Override
+                                                       public FileHeader call() throws Exception {
+                                                               return getService().updateFileContents(u.getId(), f.getId(), contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath());
+                                                       }
+                                               });
+                                       updateAccounting(owner, new Date(), fileLocal.getCurrentBody().getFileSize());
                                        getService().removeFileUploadProgress(user.getId(), fileName);
                                }
                        }
@@ -880,44 +1042,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<FileHeaderDTO> deletedFiles = new ArrayList<FileHeaderDTO>();
-               try{
-                       deletedFiles = getService().getDeletedFiles(file.getOwner().getId());
-               }
-               catch(ObjectNotFoundException e){
-
-               } catch (RpcException e) {
-
-               }
-               List<FileHeaderDTO> filesInSameFolder = new ArrayList<FileHeaderDTO>();
-               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.
         *
@@ -928,7 +1061,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 {
@@ -964,12 +1097,27 @@ public class FilesHandler extends RequestHandler {
                }
 
                try {
-                       if (resource instanceof FolderDTO) {
-                               FolderDTO folder = (FolderDTO) resource;
-                               getService().moveFolderToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
+                       final User dOwner = destOwner;
+                       final String dest = destination;
+                       if (resource instanceof Folder) {
+                               final Folder folderLocal = (Folder) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().moveFolderToPath(user.getId(), dOwner.getId(), folderLocal.getId(), dest);
+                                               return null;
+                                       }
+                               });
                        } else {
-                               FileHeaderDTO file = (FileHeaderDTO) resource;
-                               getService().moveFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
+                               final FileHeader fileLocal = (FileHeader) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().moveFileToPath(user.getId(), dOwner.getId(), fileLocal.getId(), dest);
+                                               return null;
+                                       }
+                               });
+
                        }
                } catch (InsufficientPermissionsException e) {
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -983,6 +1131,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);
                }
        }
 
@@ -996,7 +1146,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 {
@@ -1013,10 +1163,10 @@ public class FilesHandler extends RequestHandler {
         User destOwner = null;
                boolean exists = true;
                try {
-                       destination = getDestinationPath(req, encodePath(copyTo));
-                       destination = URLDecoder.decode(destination, "UTF-8");
+                       String destinationEncoded = getDestinationPath(req, encodePath(copyTo));
+                       destination = URLDecoder.decode(destinationEncoded, "UTF-8");
                        destOwner = getDestinationOwner(req);
-                       getService().getResourceAtPath(destOwner.getId(), destination, true);
+                       getService().getResourceAtPath(destOwner.getId(), destinationEncoded, true);
                } catch (ObjectNotFoundException e) {
                        exists = false;
                } catch (URISyntaxException e) {
@@ -1032,12 +1182,26 @@ public class FilesHandler extends RequestHandler {
                }
 
                try {
-                       if (resource instanceof FolderDTO) {
-                               FolderDTO folder = (FolderDTO) resource;
-                               getService().copyFolderStructureToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
+                       final User dOwner = destOwner;
+                       final String dest = destination;
+                       if (resource instanceof Folder) {
+                               final Folder folderLocal = (Folder) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().copyFolderStructureToPath(user.getId(), dOwner.getId(), folderLocal.getId(), dest);
+                                               return null;
+                                       }
+                               });
                        } else {
-                               FileHeaderDTO file = (FileHeaderDTO) resource;
-                               getService().copyFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
+                               final FileHeader fileLocal = (FileHeader) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().copyFileToPath(user.getId(), dOwner.getId(), fileLocal.getId(), dest);
+                                               return null;
+                                       }
+                               });
                        }
                } catch (InsufficientPermissionsException e) {
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -1051,6 +1215,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);
                }
        }
 
@@ -1068,6 +1234,7 @@ public class FilesHandler extends RequestHandler {
        /**
         * A helper method that extracts the relative resource path,
         * after removing the 'files' namespace.
+        * The path returned is <i>not</i> URL-decoded.
         *
         * @param req the HTTP request
         * @param path the specified path
@@ -1075,10 +1242,11 @@ public class FilesHandler extends RequestHandler {
         * @throws URISyntaxException
         * @throws RpcException in case an error occurs while communicating
         *                                              with the backend
+        * @throws UnsupportedEncodingException
         */
-       private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException {
+       private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException, UnsupportedEncodingException {
                URI uri = new URI(path);
-               String dest = uri.getPath();
+               String dest = uri.getRawPath();
                // Remove the context path from the destination URI.
                String contextPath = req.getContextPath();
                if (!dest.startsWith(contextPath))
@@ -1095,7 +1263,8 @@ public class FilesHandler extends RequestHandler {
                int slash = dest.substring(1).indexOf('/');
                if (slash == -1)
                        throw new URISyntaxException(dest, "No username in the destination URI");
-               String owner = dest.substring(1, slash + 1);
+               // Decode the user to get the proper characters (mainly the @)
+               String owner = URLDecoder.decode(dest.substring(1, slash + 1), "UTF-8");
                User o;
                o = getService().findUser(owner);
                if (o == null)
@@ -1120,7 +1289,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 {
@@ -1134,12 +1303,24 @@ public class FilesHandler extends RequestHandler {
                }
 
                try {
-                       if (resource instanceof FolderDTO) {
-                               FolderDTO folder = (FolderDTO) resource;
-                               getService().moveFolderToTrash(user.getId(), folder.getId());
+                       if (resource instanceof Folder) {
+                               final Folder folderLocal = (Folder) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().moveFolderToTrash(user.getId(), folderLocal.getId());
+                                               return null;
+                                       }
+                               });
                        } else {
-                               FileHeaderDTO file = (FileHeaderDTO) resource;
-                               getService().moveFileToTrash(user.getId(), file.getId());
+                               final FileHeader fileLocal = (FileHeader) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().moveFileToTrash(user.getId(), fileLocal.getId());
+                                               return null;
+                                       }
+                               });
                        }
                } catch (InsufficientPermissionsException e) {
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -1147,6 +1328,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);
                }
        }
 
@@ -1159,7 +1342,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 {
@@ -1173,12 +1356,24 @@ public class FilesHandler extends RequestHandler {
                }
 
                try {
-                       if (resource instanceof FolderDTO) {
-                               FolderDTO folder = (FolderDTO) resource;
-                               getService().removeFolderFromTrash(user.getId(), folder.getId());
+                       if (resource instanceof Folder) {
+                               final Folder folderLocal = (Folder) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().removeFolderFromTrash(user.getId(), folderLocal.getId());
+                                               return null;
+                                       }
+                               });
                        } else {
-                               FileHeaderDTO file = (FileHeaderDTO) resource;
-                               getService().removeFileFromTrash(user.getId(), file.getId());
+                               final FileHeader fileLocal = (FileHeader) resource;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @Override
+                                       public Void call() throws Exception {
+                                               getService().removeFileFromTrash(user.getId(), fileLocal.getId());
+                                               return null;
+                                       }
+                               });
                        }
                } catch (InsufficientPermissionsException e) {
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -1186,6 +1381,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);
                }
        }
 
@@ -1198,9 +1395,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) {
@@ -1210,49 +1408,65 @@ 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;
+                       if (resource instanceof Folder) {
+                               final Folder folderLocal = (Folder) 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);
-                               }
-
+                               if (!isValidResourceName(name)) {
+                               resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                               return;
+                       }
                                JSONArray permissions = json.optJSONArray("permissions");
-                               if (permissions != null) {
-                                       Set<PermissionDTO> perms = parsePermissions(user, permissions);
-                                       getService().setFolderPermissions(user.getId(), folder.getId(), perms);
+                               Set<Permission> 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<Permission> fPerms = perms;
+                                       Folder folderUpdated = new TransactionHelper<Folder>().tryExecute(new Callable<Folder>() {
+                                               @Override
+                                               public Folder call() throws Exception {
+                                                       return getService().updateFolder(user.getId(), folderLocal.getId(), fName, freadForAll, fPerms);
+                                               }
+
+                                       });
+                                       resp.getWriter().println(getNewUrl(req, folderUpdated));
                                }
                        } else {
-                               FileHeaderDTO file = (FileHeaderDTO) resource;
+                               final FileHeader fileLocal = (FileHeader) resource;
                                String name = null;
                                if (json.opt("name") != null)
                                        name = json.optString("name");
+                               if (name != null)
+                                       if (!isValidResourceName(name)) {
+                                       resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                                       return;
+                               }
+                               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();
@@ -1261,22 +1475,32 @@ 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<PermissionDTO> perms = null;
+                               Set<Permission> perms = null;
                                if (permissions != null)
                                        perms = parsePermissions(user, permissions);
                                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<Permission> fPerms = perms;
+                                       new TransactionHelper<Object>().tryExecute(new Callable<Object>() {
+                                               @Override
+                                               public Object call() throws Exception {
+                                                       getService().updateFile(user.getId(), fileLocal.getId(),
+                                                                               fName, fTags, mDate, fVersioned,
+                                                                               fReadForAll, fPerms);
+                                                       return null;
+                                               }
+
+                                       });
                                }
                        }
                } catch (JSONException e) {
@@ -1289,12 +1513,29 @@ 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, Folder 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.
+        * Permission objects.
         *
         * @param user the current user
         * @param permissions the JSON array to parse
@@ -1302,15 +1543,16 @@ 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<PermissionDTO> parsePermissions(User user, JSONArray permissions)
-                       throws JSONException, RpcException, ObjectNotFoundException {
+       private Set<Permission> parsePermissions(User user, JSONArray permissions)
+                       throws JSONException, RpcException, ObjectNotFoundException, UnsupportedEncodingException {
                if (permissions == null)
                        return null;
-               Set<PermissionDTO> perms = new HashSet<PermissionDTO>();
+               Set<Permission> perms = new HashSet<Permission>();
                for (int i = 0; i < permissions.length(); i++) {
                        JSONObject j = permissions.getJSONObject(i);
-                       PermissionDTO perm = new PermissionDTO();
+                       Permission perm = new Permission();
                        perm.setModifyACL(j.optBoolean("modifyACL"));
                        perm.setRead(j.optBoolean("read"));
                        perm.setWrite(j.optBoolean("write"));
@@ -1319,15 +1561,26 @@ public class FilesHandler extends RequestHandler {
                                User u = getService().findUser(permUser);
                                if (u == null)
                                        throw new ObjectNotFoundException("User " + permUser + " not found");
-                               perm.setUser(u.getDTO());
+                               perm.setUser(u);
                        }
+                       // 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()) {
-                               GroupDTO g = getService().getGroup(user.getId(), permGroup);
+                       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");
+                               Group g = getService().getGroup(u.getId(), grp);
                                perm.setGroup(g);
                        }
-                       if (permUser.isEmpty() && permGroup.isEmpty() ||
-                                               permUser.isEmpty() && permGroup.isEmpty())
+                       else if (!permGroup.isEmpty()) {
+                               Group g = getService().getGroup(user.getId(), permGroup);
+                               perm.setGroup(g);
+                       }
+                       if (permUser.isEmpty() && permGroupUri.isEmpty() && permGroup.isEmpty())
                                throw new JSONException("A permission must correspond to either a user or a group");
                        perms.add(perm);
                }
@@ -1343,11 +1596,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 {
@@ -1377,10 +1630,16 @@ public class FilesHandler extends RequestHandler {
                        return;
                }
                try {
-                       if (parent instanceof FolderDTO) {
-                               FolderDTO folder = (FolderDTO) parent;
-                               getService().createFolder(user.getId(), folder.getId(), folderName);
-                       String newResource = getContextPath(req, true) + folderName;
+                       if (parent instanceof Folder) {
+                               final Folder folderLocal = (Folder) parent;
+                               Folder newFolder = new TransactionHelper<Folder>().tryExecute(new Callable<Folder>() {
+                                       @Override
+                                       public Folder call() throws Exception {
+                                               return getService().createFolder(user.getId(), folderLocal.getId(), folderName);
+                                       }
+
+                               });
+                       String newResource = getApiRoot() + newFolder.getURI();
                        resp.setHeader("Location", newResource);
                        resp.setContentType("text/plain");
                    PrintWriter out = resp.getWriter();
@@ -1401,6 +1660,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);
        }
@@ -1422,11 +1684,11 @@ public class FilesHandler extends RequestHandler {
        if (logger.isDebugEnabled())
                        logger.debug("Updating resource: " + path);
 
-       User user = getUser(req);
+       final User user = getUser(req);
        User owner = getOwner(req);
        boolean exists = true;
         Object resource = null;
-        FileHeaderDTO file = null;
+        FileHeader fileLocal = null;
         try {
                resource = getService().getResourceAtPath(owner.getId(), path, false);
         } catch (ObjectNotFoundException e) {
@@ -1437,8 +1699,8 @@ public class FilesHandler extends RequestHandler {
                }
 
         if (exists)
-                       if (resource instanceof FileHeaderDTO)
-                       file = (FileHeaderDTO) resource;
+                       if (resource instanceof FileHeader)
+                       fileLocal = (FileHeader) resource;
                        else {
                        resp.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
                        return;
@@ -1474,28 +1736,42 @@ public class FilesHandler extends RequestHandler {
                        resourceInputStream = req.getInputStream();
 
         try {
-               FolderDTO folder = null;
+               Folder folderLocal = null;
                Object parent = getService().getResourceAtPath(owner.getId(), getParentPath(path), true);
-               if (!(parent instanceof FolderDTO)) {
+               if (!(parent instanceof Folder)) {
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
                        return;
                }
-                       folder = (FolderDTO) parent;
-               String name = getLastElement(path);
-               String mimeType = context.getMimeType(name);
+                       folderLocal = (Folder) parent;
+               final String name = getLastElement(path);
+               final String mimeType = context.getMimeType(name);
                File uploadedFile = null;
                try {
                                uploadedFile = getService().uploadFile(resourceInputStream, user.getId());
                        } catch (IOException ex) {
                                throw new GSSIOException(ex, false);
                        }
-               FileHeaderDTO fileDTO = null;
+               FileHeader fileTemp = null;
+               final File uploadedf = uploadedFile;
+                       final Folder parentf = folderLocal;
+                       final FileHeader f = fileLocal;
             if (exists)
-               fileDTO = getService().updateFileContents(user.getId(), file.getId(), mimeType, uploadedFile);
+               fileTemp = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() {
+                                       @Override
+                                       public FileHeader call() throws Exception {
+                                               return getService().updateFileContents(user.getId(), f.getId(), mimeType, uploadedf.getCanonicalFile().length(), uploadedf.getAbsolutePath());
+                                       }
+                               });
                        else
-                               fileDTO = getService().createFile(user.getId(), folder.getId(), name, mimeType, uploadedFile);
-            getService().updateAccounting(user, new Date(), fileDTO.getFileSize());
-                       getService().removeFileUploadProgress(user.getId(), fileDTO.getName());
+                               fileTemp = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() {
+                                       @Override
+                                       public FileHeader call() throws Exception {
+                                               return getService().createFile(user.getId(), parentf.getId(), name, mimeType, uploadedf.getCanonicalFile().length(), uploadedf.getAbsolutePath());
+                                       }
+
+                               });
+            updateAccounting(owner, new Date(), fileTemp.getCurrentBody().getFileSize());
+                       getService().removeFileUploadProgress(user.getId(), fileTemp.getName());
         } catch(ObjectNotFoundException e) {
             result = false;
         } catch (RpcException e) {
@@ -1516,6 +1792,9 @@ public class FilesHandler extends RequestHandler {
                } catch (QuotaExceededException e) {
                        resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
                return;
+               } catch (Exception e) {
+               resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
+                       return;
                }
 
         if (result) {
@@ -1539,7 +1818,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;
@@ -1557,16 +1836,23 @@ public class FilesHandler extends RequestHandler {
                return;
        }
 
-       FolderDTO folder = null;
-       FileHeaderDTO file = null;
-       if (object instanceof FolderDTO)
-               folder = (FolderDTO) object;
+       Folder folderLocal = null;
+       FileHeader fileLocal = null;
+       if (object instanceof Folder)
+               folderLocal = (Folder) object;
        else
-               file = (FileHeaderDTO) object;
+               fileLocal = (FileHeader) object;
 
-       if (file != null)
+       if (fileLocal != null)
                        try {
-                               getService().deleteFile(user.getId(), file.getId());
+                               final FileHeader f = fileLocal;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @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;
@@ -1578,10 +1864,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)
+               else if (folderLocal != null)
                        try {
-                       getService().deleteFolder(user.getId(), folder.getId());
+                               final Folder fo = folderLocal;
+                               new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
+                                       @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;
@@ -1591,6 +1887,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;
@@ -1608,15 +1907,27 @@ public class FilesHandler extends RequestHandler {
         * @throws InsufficientPermissionsException if the user does not have
         *                      the necessary privileges to read the directory
      */
-    private InputStream renderJson(User user, FolderDTO folder) throws IOException,
+    private InputStream renderJson(User user, Folder folder) throws IOException,
                ServletException, InsufficientPermissionsException {
+       try {
+                       folder = getService().expandFolder(folder);
+               } catch (ObjectNotFoundException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               } catch (RpcException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               }
        JSONObject json = new JSONObject();
        try {
                        json.put("name", folder.getName()).
                                        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("shared", folder.getShared()).
+                                       put("readForAll", folder.isReadForAll());
+
                        if (folder.getAuditInfo().getModifiedBy() != null)
                                json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
                                                put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
@@ -1627,7 +1938,7 @@ public class FilesHandler extends RequestHandler {
                                json.put("parent", j);
                        }
                List<JSONObject> subfolders = new ArrayList<JSONObject>();
-               for (FolderDTO f: folder.getSubfolders())
+               for (Folder f: folder.getSubfolders())
                                if (!f.isDeleted()) {
                                        JSONObject j = new JSONObject();
                                        j.put("name", f.getName()).
@@ -1636,15 +1947,17 @@ public class FilesHandler extends RequestHandler {
                                }
                json.put("folders", subfolders);
                List<JSONObject> files = new ArrayList<JSONObject>();
-               List<FileHeaderDTO> fileHeaders = getService().getFiles(user.getId(), folder.getId(), false);
-               for (FileHeaderDTO f: fileHeaders) {
+               List<FileHeader> fileHeaders = getService().getFiles(user.getId(), folder.getId(), false);
+               for (FileHeader f: fileHeaders) {
                        JSONObject j = new JSONObject();
                                j.put("name", f.getName()).
                                        put("owner", f.getOwner().getUsername()).
                                        put("deleted", f.isDeleted()).
-                                       put("version", f.getVersion()).
-                                       put("content", f.getMimeType()).
-                                       put("size", f.getFileSize()).
+                                       put("version", f.getCurrentBody().getVersion()).
+                                       put("content", f.getCurrentBody().getMimeType()).
+                                       put("size", f.getCurrentBody().getFileSize()).
+                                       put("shared", f.getShared()).
+                                       put("versioned",f.isVersioned()).
                                        put("creationDate", f.getAuditInfo().getCreationDate().getTime()).
                                        put("path", f.getFolder().getPath()).
                                        put("uri", getApiRoot() + f.getURI());
@@ -1653,7 +1966,7 @@ public class FilesHandler extends RequestHandler {
                                files.add(j);
                }
                json.put("files", files);
-               Set<PermissionDTO> perms = getService().getFolderPermissions(user.getId(), folder.getId());
+               Set<Permission> perms = getService().getFolderPermissions(user.getId(), folder.getId());
                json.put("permissions", renderJson(perms));
                } catch (JSONException e) {
                        throw new ServletException(e);
@@ -1681,7 +1994,7 @@ public class FilesHandler extends RequestHandler {
         * @throws InsufficientPermissionsException
         * @throws ObjectNotFoundException
      */
-    private String renderJsonMetadata(User user, FolderDTO folder)
+    private String renderJsonMetadata(User user, Folder folder)
                throws ServletException, InsufficientPermissionsException {
        // Check if the user has read permission.
                try {
@@ -1695,7 +2008,7 @@ public class FilesHandler extends RequestHandler {
 
        JSONObject json = new JSONObject();
        try {
-                       json.put("name", folder.getName()).
+                       json.put("name", URLEncoder.encode(folder.getName(), "UTF-8")).
                        put("owner", folder.getOwner().getUsername()).
                        put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
                        put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
@@ -1706,7 +2019,10 @@ public class FilesHandler extends RequestHandler {
                } catch (JSONException e) {
                        throw new ServletException(e);
                }
-       return json.toString();
+        catch (UnsupportedEncodingException e) {
+            throw new ServletException(e);
+        }
+        return json.toString();
     }
 
        /**
@@ -1722,17 +2038,19 @@ public class FilesHandler extends RequestHandler {
         * @throws InsufficientPermissionsException if the user does not have
         *                      the necessary privileges to read the directory
      */
-    private String renderJson(User user, FileHeaderDTO file, FileBodyDTO oldBody)
+    private String renderJson(User user, FileHeader file, FileBody oldBody)
                throws ServletException, InsufficientPermissionsException {
        JSONObject json = new JSONObject();
        try {
+               file=getService().expandFile(file);
                // Need to encode file name in order to properly display it in the web client.
                        json.put("name", URLEncoder.encode(file.getName(),"UTF-8")).
                                        put("owner", file.getOwner().getUsername()).
                                        put("versioned", file.isVersioned()).
-                                       put("version", oldBody != null ? oldBody.getVersion() : file.getVersion()).
+                                       put("version", oldBody != null ? oldBody.getVersion() : file.getCurrentBody().getVersion()).
                                        put("readForAll", file.isReadForAll()).
-                                       put("tags", renderJson(file.getTags())).
+                                       put("shared", file.getShared()).
+                                       put("tags", renderJson(file.getFileTagsAsStrings())).
                                        put("path", file.getFolder().getPath()).
                                put("uri", getApiRoot() + file.getURI()).
                                        put("deleted", file.isDeleted());
@@ -1752,9 +2070,9 @@ public class FilesHandler extends RequestHandler {
                                                put("creationDate", file.getAuditInfo().getCreationDate().getTime()).
                                                put("modifiedBy", file.getAuditInfo().getModifiedBy().getUsername()).
                                                put("modificationDate", file.getAuditInfo().getModificationDate().getTime()).
-                                               put("content", file.getMimeType()).
-                                               put("size", file.getFileSize());
-               Set<PermissionDTO> perms = getService().getFilePermissions(user.getId(), file.getId());
+                                               put("content", file.getCurrentBody().getMimeType()).
+                                               put("size", file.getCurrentBody().getFileSize());
+               Set<Permission> perms = getService().getFilePermissions(user.getId(), file.getId());
                json.put("permissions", renderJson(perms));
                } catch (JSONException e) {
                        throw new ServletException(e);
@@ -1778,15 +2096,18 @@ public class FilesHandler extends RequestHandler {
         * @throws JSONException
         * @throws UnsupportedEncodingException
         */
-       private JSONArray renderJson(Set<PermissionDTO> permissions) throws JSONException, UnsupportedEncodingException {
+       private JSONArray renderJson(Set<Permission> permissions) throws JSONException, UnsupportedEncodingException {
                JSONArray perms = new JSONArray();
-               for (PermissionDTO p: permissions) {
+               for (Permission p: permissions) {
                        JSONObject permission = new JSONObject();
                        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) {
+                               Group 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;
@@ -1830,8 +2151,6 @@ public class FilesHandler extends RequestHandler {
 
                private long fileSize = -100;
 
-               private long tenKBRead = -1;
-
                private Long userId;
 
                private String filename;
@@ -1860,13 +2179,13 @@ 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;
                        if (fileSize != contentLength)
                                fileSize = contentLength;
                        int percent = new Long(bytesTransferred * 100 / fileSize).intValue();
-
                        if (percent < 5 || percent % TRACK_PROGRESS_PERCENT == 0 )
                                if (percent != percentLogged){
                                        percentLogged = percent;
@@ -1880,4 +2199,172 @@ 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 path the requested path to the resource
+        * @param folder the specified directory
+        * @param user the specified user
+        * @return an input stream with the rendered contents
+        * @throws IOException
+        * @throws ServletException
+        */
+       private InputStream renderHtml(String contextPath, String path, Folder 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("<html>\r\n");
+               sb.append("<head>\r\n");
+               sb.append("<title>");
+               sb.append("Index of " + name);
+               sb.append("</title>\r\n");
+               sb.append("<STYLE><!--");
+               sb.append(GSS_CSS);
+               sb.append("--></STYLE> ");
+               sb.append("</head>\r\n");
+               sb.append("<body>");
+               sb.append("<h1>");
+               sb.append("Index of " + name);
+
+               // Render the link to our parent (if required)
+               String parentDirectory = path;
+               if (parentDirectory.endsWith("/"))
+                       parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1);
+               int slash = parentDirectory.lastIndexOf('/');
+               if (slash >= 0) {
+                       String parent = path.substring(0, slash);
+                       sb.append(" - <a href=\"");
+                       sb.append(rewrittenContextPath);
+                       if (parent.equals(""))
+                               parent = "/";
+                       sb.append(parent);
+                       if (!parent.endsWith("/"))
+                               sb.append("/");
+                       sb.append("\">");
+                       sb.append("<b>");
+                       sb.append("Up To " + parent);
+                       sb.append("</b>");
+                       sb.append("</a>");
+               }
+
+               sb.append("</h1>");
+               sb.append("<HR size=\"1\" noshade=\"noshade\">");
+
+               sb.append("<table width=\"100%\" cellspacing=\"0\"" + " cellpadding=\"5\" align=\"center\">\r\n");
+
+               // Render the column headings
+               sb.append("<tr>\r\n");
+               sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
+               sb.append("Name");
+               sb.append("</strong></font></td>\r\n");
+               sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
+               sb.append("Size");
+               sb.append("</strong></font></td>\r\n");
+               sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
+               sb.append("Last modified");
+               sb.append("</strong></font></td>\r\n");
+               sb.append("</tr>");
+               // Render the directory entries within this directory
+               boolean shade = false;
+               Iterator iter = folder.getSubfolders().iterator();
+               while (iter.hasNext()) {
+                       Folder subf = (Folder) iter.next();
+                       if(subf.isReadForAll() && !subf.isDeleted()){
+                               String resourceName = subf.getName();
+                               if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF"))
+                                       continue;
+
+                               sb.append("<tr");
+                               if (shade)
+                                       sb.append(" bgcolor=\"#eeeeee\"");
+                               sb.append(">\r\n");
+                               shade = !shade;
+
+                               sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
+                               sb.append("<a href=\"");
+                               sb.append(rewrittenContextPath+path);
+                               sb.append(rewriteUrl(resourceName));
+                               sb.append("/");
+                               sb.append("\"><tt>");
+                               sb.append(RequestUtil.filter(resourceName));
+                               sb.append("/");
+                               sb.append("</tt></a></td>\r\n");
+
+                               sb.append("<td align=\"right\"><tt>");
+                               sb.append("&nbsp;");
+                               sb.append("</tt></td>\r\n");
+
+                               sb.append("<td align=\"right\"><tt>");
+                               sb.append(getLastModifiedHttp(folder.getAuditInfo()));
+                               sb.append("</tt></td>\r\n");
+
+                               sb.append("</tr>\r\n");
+
+                       }
+               }
+               List<FileHeader> 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 (FileHeader file : files)
+               //Display only file resources that are marked as public and are not deleted
+                       if(file.isReadForAll() && !file.isDeleted()){
+                               String resourceName = file.getName();
+                               if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF"))
+                                       continue;
+
+                               sb.append("<tr");
+                               if (shade)
+                                       sb.append(" bgcolor=\"#eeeeee\"");
+                               sb.append(">\r\n");
+                               shade = !shade;
+
+                               sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
+                               sb.append("<a href=\"");
+                               sb.append(rewrittenContextPath + path);
+                               sb.append(rewriteUrl(resourceName));
+                               sb.append("\"><tt>");
+                               sb.append(RequestUtil.filter(resourceName));
+                               sb.append("</tt></a></td>\r\n");
+
+                               sb.append("<td align=\"right\"><tt>");
+                               sb.append(renderSize(file.getCurrentBody().getFileSize()));
+                               sb.append("</tt></td>\r\n");
+
+                               sb.append("<td align=\"right\"><tt>");
+                               sb.append(getLastModifiedHttp(file.getAuditInfo()));
+                               sb.append("</tt></td>\r\n");
+
+                               sb.append("</tr>\r\n");
+                       }
+
+               // Render the page footer
+               sb.append("</table>\r\n");
+
+               sb.append("<HR size=\"1\" noshade=\"noshade\">");
+               sb.append("</body>\r\n");
+               sb.append("</html>\r\n");
+
+               // Return an input stream to the underlying bytes
+               writer.write(sb.toString());
+               writer.flush();
+               return new ByteArrayInputStream(stream.toByteArray());
+
+       }
 }