Added special message for AUTH users
[pithos] / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java
index 127f640..cc65ee5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.
+ * Copyright 2007, 2008, 2009, 2010  Electronic Business Systems Ltd.
  *
  * This file is part of GSS.
  *
 package gr.ebs.gss.server.ejb;
 
 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
+
+import gr.ebs.gss.admin.client.ui.UsersTable;
 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.InvitationUsedException;
 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
 import gr.ebs.gss.client.exceptions.QuotaExceededException;
 import gr.ebs.gss.server.domain.AuditInfo;
@@ -31,14 +34,14 @@ import gr.ebs.gss.server.domain.FileTag;
 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.FileLock;
+import gr.ebs.gss.server.domain.Invitation;
 import gr.ebs.gss.server.domain.Nonce;
 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.domain.UserClass;
+import gr.ebs.gss.server.domain.UserLogin;
+import gr.ebs.gss.server.domain.WebDavNonce;
 import gr.ebs.gss.server.domain.dto.StatsDTO;
 import gr.ebs.gss.server.domain.dto.UserDTO;
 
@@ -48,20 +51,21 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Random;
 import java.util.Set;
 import java.util.StringTokenizer;
 
 import javax.ejb.EJB;
 import javax.ejb.EJBException;
+import javax.ejb.EJBTransactionRolledbackException;
 import javax.ejb.Stateless;
 import javax.ejb.TransactionAttribute;
 import javax.ejb.TransactionAttributeType;
@@ -73,35 +77,31 @@ import javax.jms.MessageProducer;
 import javax.jms.Queue;
 import javax.jms.QueueConnectionFactory;
 import javax.jms.Session;
-import javax.jws.WebMethod;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.NameValuePair;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.commons.httpclient.methods.PostMethod;
-import org.apache.commons.httpclient.methods.StringRequestEntity;
+import javax.persistence.PersistenceException;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
+import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrInputDocument;
+import org.hibernate.exception.ConstraintViolationException;
+
+import com.novell.ldap.LDAPAttribute;
+import com.novell.ldap.LDAPAttributeSet;
+import com.novell.ldap.LDAPConnection;
+import com.novell.ldap.LDAPEntry;
+import com.novell.ldap.LDAPException;
 
 /**
  * The concrete implementation of the ExternalAPI interface.
@@ -138,20 +138,36 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         */
        private static Random random = new Random();
 
+       /**
+        * Mark the folder and all of its parent folders as modified from the specified user.
+        */
+       private void touchParentFolders(Folder folder, User user, Date date) {
+               Folder f = folder;
+               while (f != null) {
+                       AuditInfo ai = f.getAuditInfo();
+                       ai.setModifiedBy(user);
+                       ai.setModificationDate(date);
+                       f.setAuditInfo(ai);
+                       f = f.getParent();
+               }
+       }
+
+       private Long getRootFolderId(Long userId) throws ObjectNotFoundException {
+               if (userId == null)
+                       throw new ObjectNotFoundException("No user specified");
+               return dao.getRootFolderId(userId);
+       }
+       
        @Override
-       public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException {
+       public Folder getRootFolder(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                Folder folder = dao.getRootFolder(userId);
-               return folder.getDTO();
+               return folder;
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolder(java.lang.Long)
-        */
-       public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
+       @Override
+       public Folder getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
@@ -161,39 +177,31 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                // Check permissions
                if (!folder.hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
-               return folder.getDTO();
+               return expandFolder(folder);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUser(java.lang.Long)
-        */
+       @Override
        public User getUser(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                return dao.getEntityById(User.class, userId);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserDTO(java.lang.Long)
-        */
+       @Override
        public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException {
                return getUser(userId).getDTO();
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroup(java.lang.Long)
-        */
-       public GroupDTO getGroup(final Long groupId) throws ObjectNotFoundException {
+       @Override
+       public Group getGroup(final Long groupId) throws ObjectNotFoundException {
                if (groupId == null)
                        throw new ObjectNotFoundException("No group specified");
                final Group group = dao.getEntityById(Group.class, groupId);
-               return group.getDTO();
+               return group;
        }
 
        @Override
-       public GroupDTO getGroup(Long userId, String name) throws ObjectNotFoundException {
+       public Group getGroup(Long userId, String name) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (name == null)
@@ -202,27 +210,20 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                List<Group> groups = user.getGroupsSpecified();
                for (Group group: groups)
                        if (group.getName().equals(name))
-                               return group.getDTO();
+                               return group;
                throw new ObjectNotFoundException("Group " + name + " not found");
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroups(java.lang.Long)
-        */
-       public List<GroupDTO> getGroups(final Long userId) throws ObjectNotFoundException {
+       @Override
+       public List<Group> getGroups(final Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                final List<Group> groups = dao.getGroups(userId);
-               final List<GroupDTO> result = new ArrayList<GroupDTO>();
-               for (final Group g : groups)
-                       result.add(g.getDTO());
-               return result;
+               return groups;
        }
 
        @Override
-       public List<FileHeaderDTO> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
+       public List<FileHeader> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
                        throws ObjectNotFoundException, InsufficientPermissionsException {
                // Validate.
                if (userId == null)
@@ -233,21 +234,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                Folder folder = dao.getEntityById(Folder.class, folderId);
                if (!folder.hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
-               // Do the actual work.
-               List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
                List<FileHeader> files = dao.getFiles(folderId, userId, ignoreDeleted);
-               for (FileHeader f : files)
-                       result.add(f.getDTO());
-               return result;
+               return files;
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsers(java.lang.Long,
-        *      java.lang.Long)
-        */
-       public List<UserDTO> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
+       @Override
+       public List<User> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -256,14 +248,11 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                // Do the actual work.
                final List<User> users = dao.getUsers(groupId);
-               final List<UserDTO> result = new ArrayList<UserDTO>();
-               for (final User u : users)
-                       result.add(u.getDTO());
-               return result;
+               return users;
        }
 
        @Override
-       public void createFolder(Long userId, Long parentId, String name)
+       public Folder createFolder(Long userId, Long parentId, String name)
                        throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
                // Validate.
                if (userId == null)
@@ -290,7 +279,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                                        " to write to this folder");
 
                // Do the actual work.
-               createFolder(name, parent, creator);
+               return createFolder(name, parent, creator);
        }
 
        /**
@@ -299,9 +288,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         * @param name
         * @param parent
         * @param creator
-        * @param owner
+        * @return the new folder
         */
-       private void createFolder(String name, Folder parent, User creator) {
+       private Folder createFolder(String name, Folder parent, User creator) {
                Folder folder = new Folder();
                folder.setName(name);
                if (parent != null) {
@@ -317,6 +306,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                auditInfo.setModifiedBy(creator);
                auditInfo.setModificationDate(now);
                folder.setAuditInfo(auditInfo);
+               touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
 
                if (parent != null)
                        for (Permission p : parent.getPermissions()) {
@@ -336,15 +326,15 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        permission.setModifyACL(true);
                        folder.addPermission(permission);
                }
+
+               if(parent != null)
+                       folder.setReadForAll(parent.isReadForAll());
+
                dao.create(folder);
+               return folder;
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFolder(java.lang.Long,
-        *      java.lang.Long)
-        */
+       @Override
        public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException {
                // Validate.
                if (userId == null)
@@ -362,12 +352,33 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        logger.info("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
                }
+               removeSubfolderFiles(folder);
                parent.removeSubfolder(folder);
                dao.delete(folder);
+               touchParentFolders(parent, user, new Date());
+       }
+
+       /**
+        * Traverses the folder and deletes all actual files (file system)
+        * regardless of permissions
+        *
+        * @param folder
+        */
+       private void removeSubfolderFiles(Folder folder) {
+               //remove files for all subfolders
+               for (Folder subfolder:folder.getSubfolders())
+                       removeSubfolderFiles(subfolder);
+               //remove this folder's file bodies (actual files)
+               for (FileHeader file:folder.getFiles()) {
+                       for (FileBody body:file.getBodies())
+                               deleteActualFile(body.getStoredFilePath());
+                       indexFile(file.getId(), true);
+               }
        }
 
+       @Override
        @SuppressWarnings("unchecked")
-       public List<FolderDTO> getSubfolders(Long userId, Long folderId)
+       public List<Folder> getSubfolders(Long userId, Long folderId)
                        throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -377,53 +388,78 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                Folder folder = dao.getEntityById(Folder.class, folderId);
                if (!folder.hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
+               List<Folder> result = new ArrayList<Folder>();
                if (folder.hasReadPermission(user))
                        for (Folder f : folder.getSubfolders())
                                if (f.hasReadPermission(user) && !f.isDeleted())
-                                       result.add(f.getDTO());
+                                       result.add(f);
                return result;
        }
 
        @Override
-       public void modifyFolder(Long userId, Long folderId, String folderName)
-                       throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException {
+       public Folder updateFolder(Long userId, Long folderId, String folderName,
+                               Boolean readForAll,
+                               Set<Permission> permissions)
+                       throws InsufficientPermissionsException, ObjectNotFoundException,
+                       DuplicateNameException {
 
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
                        throw new ObjectNotFoundException("No folder specified");
-               if (StringUtils.isEmpty(folderName))
-                       throw new ObjectNotFoundException("New folder name is empty");
 
                Folder folder = dao.getEntityById(Folder.class, folderId);
                User user = dao.getEntityById(User.class, userId);
-               if (!folder.hasWritePermission(user))
+               if (folderName != null && !folder.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
+               if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
+               // Check permissions for making file public.
+               if (readForAll != null && !user.equals(folder.getOwner()))
+                               throw new InsufficientPermissionsException("Only the owner can make a folder public or not public");
 
                Folder parent = folder.getParent();
-               if (parent != null)
-                       if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
-                               throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
+               if (folderName != null) {
+                       if (parent != null)
+                               if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
+                                       throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
 
-               // Do the actual modification.
-               folder.setName(folderName);
+                       // Do the actual modification.
+                       folder.setName(folderName);
+               }
+               if (permissions != null)
+                       setFolderPermissions(user, folder, permissions);
+               if (readForAll != null)
+                       setFolderReadForAll(user, folder, readForAll);
+               folder.getAuditInfo().setModificationDate(new Date());
+               folder.getAuditInfo().setModifiedBy(user);
                dao.update(folder);
+               touchParentFolders(folder, user, new Date());
+               // Re-index the folder contents if it was modified.
+               if ((permissions != null && !permissions.isEmpty()) || readForAll != null) {
+            indexFolder(folder);
+        }
+
+               return folder;
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#createGroup(java.lang.Long,
-        *      java.lang.String)
-        */
+    private void indexFolder(Folder folder) {
+        for (FileHeader fh : folder.getFiles())
+            indexFile(fh.getId(), false);
+        for (Folder f : folder.getSubfolders())
+            indexFolder(f);
+    }
+
+       @Override
        public void createGroup(final Long userId, final String name) throws ObjectNotFoundException, DuplicateNameException {
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (StringUtils.isEmpty(name))
                        throw new ObjectNotFoundException("New group name is empty");
+               if (name.indexOf('/')>=0)
+                       throw new IllegalArgumentException("Character '/' is not allowed in group name");
                if (dao.existsGroup(userId, name))
                        throw new DuplicateNameException("A group with the name '" + name + "' already exists");
 
@@ -435,12 +471,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                owner.createGroup(name);
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteGroup(java.lang.Long,
-        *      java.lang.Long)
-        */
+       @Override
        public void deleteGroup(final Long userId, final Long groupId) throws ObjectNotFoundException, InsufficientPermissionsException {
                // Validate.
                if (userId == null)
@@ -451,17 +482,20 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                // Do the actual work.
                final User owner = dao.getEntityById(User.class, userId);
                final Group group = dao.getEntityById(Group.class, groupId);
+               final Date now = new Date();
                // Only delete the group if actually owned by the user.
                if (group.getOwner().equals(owner)) {
                        List<Folder> folders = dao.getFoldersPermittedForGroup(userId, groupId);
                        for (Folder f : folders){
                                f.getPermissions().removeAll(group.getPermissions());
-                               for(FileHeader file : f.getFiles())
+                               for(FileHeader file : f.getFiles()){
                                        file.getPermissions().removeAll(group.getPermissions());
+                               }
                        }
-                       List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
-                       for(FileHeader h : files)
+                       List<FileHeader> files = dao.getFilesPermittedForGroup(userId, groupId);
+                       for(FileHeader h : files){
                                h.getPermissions().removeAll(group.getPermissions());
+                       }
                        owner.removeSpecifiedGroup(group);
                        dao.delete(group);
                }
@@ -469,7 +503,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, InputStream stream)
+       public FileHeader createFile(Long userId, Long folderId, String name, String mimeType, InputStream stream)
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
                        InsufficientPermissionsException, QuotaExceededException {
                File file = null;
@@ -479,13 +513,17 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        // Supply a more accurate problem description.
                        throw new GSSIOException("Problem creating file",ioe);
                }
-               return createFile(userId, folderId, name, mimeType, file);
+               finally{
+                       try {
+                               stream.close();
+                       } catch (IOException e) {
+                               logger.error("Unable to close InputStream on FileUpload:",e);
+                       }
+               }
+               return createFile(userId, folderId, name, mimeType, file.length(), file.getAbsolutePath());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPIRemote#indexFile(java.lang.Long, boolean)
-        */
-       public void indexFile(Long fileId, boolean delete) {
+       private void indexFile(Long fileId, boolean delete) {
                Connection qConn = null;
                Session session = null;
                MessageProducer sender = null;
@@ -550,12 +588,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return secondFolder + File.separator + filename;
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFile(java.lang.Long,
-        *      java.lang.Long)
-        */
+       @Override
        public void deleteFile(final Long userId, final Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
                // Validate.
                if (userId == null)
@@ -571,21 +604,23 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                final User user = dao.getEntityById(User.class, userId);
                if (!file.hasDeletePermission(user))
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
-               for (final FileBody body : file.getBodies()) {
-                       final File fileContents = new File(body.getStoredFilePath());
-                       if (!fileContents.delete())
-                               logger.error("Could not delete file " + body.getStoredFilePath());
-               }
+               for (final FileBody body : file.getBodies())
+                       deleteActualFile(body.getStoredFilePath());
                dao.delete(file);
+               touchParentFolders(parent, user, new Date());
                indexFile(fileId, true);
        }
 
-       /*
-        * (non-Javadoc)
-        *
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#createTag(java.lang.Long,
-        *      java.lang.Long, java.lang.String)
-        */
+       @Override
+       public void deleteActualFile(String path) {
+               if (path == null)
+                       return;
+               File file = new File(path);
+               if (!file.delete())
+                       logger.error("Could not delete file " + path + " "+file.exists());
+       }
+
+       @Override
        public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -596,34 +631,57 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                final User user = dao.getEntityById(User.class, userId);
                final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId);
+               final Folder parent = fh.getFolder();
+               if (parent == null)
+                       throw new ObjectNotFoundException("The specified file has no parent folder");
                user.addTag(fh, tag);
+               touchParentFolders(parent, user, new Date());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserTags(java.lang.Long)
-        */
-       @WebMethod(operationName = "getUserTags")
+       @Override
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
                return dao.getUserTags(userId);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFile(java.lang.Long, java.lang.Long, java.lang.String, java.util.Set)
-        */
-       public void updateFile(Long userId, Long fileId, String name, String tagSet) throws ObjectNotFoundException, InsufficientPermissionsException {
+       @Override
+       public void updateFile(Long userId, Long fileId, String name,
+                               String tagSet, Date modificationDate, Boolean versioned,
+                               Boolean readForAll,     Set<Permission> permissions)
+                       throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
                        throw new ObjectNotFoundException("No file specified");
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-               User user = dao.getEntityById(User.class, userId);
-               if (!file.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getId() + " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               final Folder parent = file.getFolder();
+               if (parent == null)
+                       throw new ObjectNotFoundException("The specified file has no parent folder");
 
-               if (name != null)
+               User user = dao.getEntityById(User.class, userId);
+               // Check permissions for modifying the file metadata.
+               if ((name != null || tagSet != null || modificationDate != null || versioned != null) && !file.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update file " + file.getName() + "(" + file.getId() + ")");
+               // Check permissions for making file public.
+               if (readForAll != null && !user.equals(file.getOwner()))
+                               throw new InsufficientPermissionsException("Only the owner can make a file public or not public");
+               // Check permissions for modifying the ACL.
+               if(permissions != null && !permissions.isEmpty() &&     !file.hasModifyACLPermission(user))
+                       throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update the permissions on file " +     file.getName() + "(" + file.getId() + ")");
+
+               if (name != null) {
+                       // Do plain check for file already exists.
+                       // Extreme concurrency case should be caught by constraint violation later.
+                       if (dao.existsFolderOrFile(parent.getId(), name)) throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
                        file.setName(name);
-               List<FileTag> tags = file.getFileTags();
+               }
+
+               if (modificationDate != null)
+                       file.getAuditInfo().setModificationDate(modificationDate);
+               else
+                       file.getAuditInfo().setModificationDate(new Date());
+               file.getAuditInfo().setModifiedBy(user);
 
+               List<FileTag> tags = file.getFileTags();
                if (tagSet != null) {
                        Iterator<FileTag> i = tags.iterator();
                        while (i.hasNext()) {
@@ -638,9 +696,35 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        while (st.hasMoreTokens())
                                new FileTag(user, file, st.nextToken().trim());
                }
+               if (versioned != null && !file.isVersioned() == versioned) {
+                       if (file.isVersioned())
+                               removeOldVersions(userId, fileId);
+                       file.setVersioned(versioned);
+               }
+               if (readForAll != null && user.equals(file.getOwner()))
+                       file.setReadForAll(readForAll);
+               if (permissions != null && !permissions.isEmpty())
+                       setFilePermissions(file, permissions);
+
+               /*
+                * Force constraint violation to manifest itself here.
+                * This should cover extreme concurrency cases that the simple check
+                * above hasn't caught.
+                */
+               try {
+                       dao.flush();
+               }
+               catch (EJBTransactionRolledbackException e) {
+                       Throwable cause = e.getCause();
+                       if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
+                               throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
+                       throw e;
+               }
+
+               touchParentFolders(parent, user, new Date());
 
                // Re-index the file if it was modified.
-               if (name != null || tagSet != null)
+               if (name != null || tagSet != null || (permissions != null && !permissions.isEmpty()) || readForAll != null)
                        indexFile(fileId, false);
        }
 
@@ -671,6 +755,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        /* (non-Javadoc)
         * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long)
         */
+       @Override
        public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -696,10 +781,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                }
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFile(java.lang.Long, java.lang.Long)
-        */
-       public FileHeaderDTO getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
+       @Override
+       public FileHeader getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -708,11 +791,11 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               return file.getDTO();
+               return file;
        }
 
        @Override
-       public FileBodyDTO getFileBody(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
+       public FileBody getFileBody(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -722,7 +805,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
                FileBody body = dao.getEntityById(FileBody.class, bodyId);
-               return body.getDTO();
+               return body;
        }
 
        @Override
@@ -742,10 +825,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        return getRootFolder(owner.getId());
                // Store the last element, since it requires special handling.
                String lastElement = pathElements.remove(pathElements.size() - 1);
-               FolderDTO cursor = getRootFolder(owner.getId());
+               
+               Folder cursor = null;
+               Long rootFolderId = getRootFolderId(owner.getId());
                // Traverse and verify the specified folder path.
                for (String pathElement : pathElements) {
-                       cursor = getFolder(cursor.getId(), pathElement);
+                       cursor = getFolder(cursor==null ? rootFolderId : cursor.getId(), pathElement);
                        if (cursor.isDeleted())
                                throw new ObjectNotFoundException("Folder " + cursor.getPath() + " not found");
                }
@@ -753,14 +838,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                // Use the lastElement to retrieve the actual resource.
                Object resource = null;
                try {
-                       FileHeaderDTO file = getFile(cursor.getId(), lastElement);
+                       FileHeader file = getFile(cursor==null ? rootFolderId : cursor.getId(), lastElement);
                        if (ignoreDeleted && file.isDeleted())
                                throw new ObjectNotFoundException("Resource not found");
                        resource = file;
                } catch (ObjectNotFoundException e) {
                        // Perhaps the requested resource is not a file, so
                        // check for folders as well.
-                       FolderDTO folder = getFolder(cursor.getId(), lastElement);
+                       Folder folder = getFolder(cursor==null ? rootFolderId : cursor.getId(), lastElement);
                        if (ignoreDeleted && folder.isDeleted())
                                throw new ObjectNotFoundException("Resource not found");
                        resource = folder;
@@ -772,7 +857,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         * Retrieve a file for the specified user that has the specified name and
         * its parent folder has id equal to folderId.
         *
-        * @param userId the ID of the current user
         * @param folderId the ID of the parent folder
         * @param name the name of the requested file
         * @return the file found
@@ -780,14 +864,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         *             found, with the exception message mentioning the precise
         *             problem
         */
-       private FileHeaderDTO getFile(Long folderId, String name) throws ObjectNotFoundException {
+       private FileHeader getFile(Long folderId, String name) throws ObjectNotFoundException {
                if (folderId == null)
                        throw new ObjectNotFoundException("No parent folder specified");
                if (StringUtils.isEmpty(name))
                        throw new ObjectNotFoundException("No file specified");
 
                FileHeader file = dao.getFile(folderId, name);
-               return file.getDTO();
+               return file;
        }
 
        /**
@@ -801,17 +885,17 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         *             found, with the exception message mentioning the precise
         *             problem
         */
-       private FolderDTO getFolder(Long parentId, String name) throws ObjectNotFoundException {
+       private Folder getFolder(Long parentId, String name) throws ObjectNotFoundException {
                if (parentId == null)
                        throw new ObjectNotFoundException("No parent folder specified");
                if (StringUtils.isEmpty(name))
                        throw new ObjectNotFoundException("No folder specified");
 
                Folder folder = dao.getFolder(parentId, name);
-               return folder.getDTO();
+               return folder;
        }
 
-       private FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, InputStream resourceInputStream) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
+       private FileHeader updateFileContents(Long userId, Long fileId, String mimeType, InputStream resourceInputStream) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
                File file = null;
                try {
                        file = uploadFile(resourceInputStream, userId);
@@ -819,7 +903,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        // Supply a more accurate problem description.
                        throw new GSSIOException("Problem creating file",ioe);
                }
-               return updateFileContents(userId, fileId, mimeType, file);
+               return updateFileContents(userId, fileId, mimeType, file.length(), file.getAbsolutePath());
        }
 
        @Override
@@ -832,9 +916,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination parent folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
        }
 
@@ -850,9 +934,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination parent folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
        }
 
@@ -872,6 +956,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                User user = dao.getEntityById(User.class, userId);
                if (!file.hasReadPermission(user) || !destination.hasWritePermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
+               boolean versioned = file.isVersioned();
                int versionsNumber = file.getBodies().size();
                FileBody oldestBody = file.getBodies().get(0);
                assert oldestBody != null;
@@ -879,6 +964,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                try {
                        createFile(user.getId(), destination.getId(), destName, oldestBody.getMimeType(), new FileInputStream(contents));
                        FileHeader copiedFile = dao.getFile(destination.getId(), destName);
+                       copiedFile.setVersioned(versioned);
                        dao.flush();
                        if (versionsNumber > 1)
                                for (int i = 1; i < versionsNumber; i++) {
@@ -907,9 +993,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                copyFolder(userId, folderId, parent.getId(), getLastElement(dest));
        }
 
@@ -943,9 +1029,9 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                copyFolderStructure(userId, folderId, parent.getId(), getLastElement(dest));
        }
 
@@ -1046,15 +1132,19 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if (parent == null)
                        throw new ObjectNotFoundException("The specified file has no parent folder");
                User user = dao.getEntityById(User.class, userId);
-               if (!file.hasDeletePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
-
-               file.setDeleted(true);
-               dao.update(file);
+        trashFile(user, file);
+        touchParentFolders(parent, user, new Date());
        }
 
+    private void trashFile(User user, FileHeader file) throws InsufficientPermissionsException {
+        if (!file.hasDeletePermission(user))
+            throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
+
+        file.setDeleted(true);
+    }
+
        @Override
-       public void moveFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, DuplicateNameException, GSSIOException, QuotaExceededException {
+       public void moveFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (ownerId == null)
@@ -1065,14 +1155,14 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination parent folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                moveFile(userId, fileId, parent.getId(), getLastElement(dest));
        }
 
        @Override
-       public void moveFile(Long userId, Long fileId, Long destId, String destName) throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException, GSSIOException, QuotaExceededException {
+       public void moveFile(Long userId, Long fileId, Long destId, String destName) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -1083,25 +1173,46 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination file name specified");
 
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
+               Folder source = file.getFolder();
                Folder destination = dao.getEntityById(Folder.class, destId);
 
                User owner = dao.getEntityById(User.class, userId);
                if (!file.hasDeletePermission(owner) || !destination.hasWritePermission(owner))
                        throw new InsufficientPermissionsException("User " + owner.getId() + " cannot move file " + file.getName() + "(" + file.getId() + ")");
-               FileBody body = file.getCurrentBody();
-               assert body != null;
-               File contents = new File(body.getStoredFilePath());
-               try {
-                       createFile(owner.getId(), destination.getId(), destName, body.getMimeType(), new FileInputStream(contents));
-               } catch (FileNotFoundException e) {
-                       throw new ObjectNotFoundException("File contents not found for file " + body.getStoredFilePath());
-               }
-               deleteFile(userId, fileId);
 
+               // if the destination folder belongs to another user:
+               if (!file.getOwner().equals(destination.getOwner())) {
+                       // (a) check if the destination quota allows the move
+                       if(getQuotaLeft(destination.getOwner().getId()) < file.getTotalSize())
+                               throw new QuotaExceededException("Not enough free space available");
+                       User newOwner = destination.getOwner();
+                       // (b) if quota OK, change the owner of the file
+                       file.setOwner(newOwner);
+                       // if the file has no permission for the new owner, add it
+                       Permission ownerPermission = null;
+                       for (final Permission p : file.getPermissions())
+                               if (p.getUser() != null)
+                                       if (p.getUser().equals(newOwner)) {
+                                               ownerPermission = p;
+                                               break;
+                                       }
+                       if (ownerPermission == null) {
+                               ownerPermission = new Permission();
+                               ownerPermission.setUser(newOwner);
+                               file.addPermission(ownerPermission);
+                       }
+                       ownerPermission.setRead(true);
+                       ownerPermission.setWrite(true);
+                       ownerPermission.setModifyACL(true);
+               }
+               // move the file to the destination folder
+               file.setFolder(destination);
+               touchParentFolders(source, owner, new Date());
+               touchParentFolders(destination, owner, new Date());
        }
 
        @Override
-       public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
+       public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (ownerId == null)
@@ -1112,34 +1223,77 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No destination specified");
 
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
-               if (!(destination instanceof FolderDTO))
+               if (!(destination instanceof Folder))
                        throw new ObjectNotFoundException("Destination parent folder not found");
-               FolderDTO parent = (FolderDTO) destination;
+               Folder parent = (Folder) destination;
                moveFolder(userId, folderId, parent.getId(), getLastElement(dest));
        }
 
        @Override
-       public void moveFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
-               // TODO Simple Move and delete of original folder, in production
-               // scenario we must first check individual files and folders permissions
-               copyFolderStructure(userId, folderId, destId, destName);
-               deleteFolder(userId, folderId);
+       public void moveFolder(Long userId, Long folderId, Long destId, String destName)
+                       throws ObjectNotFoundException, InsufficientPermissionsException,
+                       QuotaExceededException {
+               Folder source = dao.getEntityById(Folder.class, folderId);
+               Folder destination = dao.getEntityById(Folder.class, destId);
+               User user = dao.getEntityById(User.class, userId);
+               User sourceOwner = source.getOwner();
+               User destinationOwner = destination.getOwner();
+               // Do not move trashed folders and contents.
+               if (source.isDeleted())
+                       return;
+               // Check permissions.
+               if (!destination.hasWritePermission(user)
+                               || !source.hasReadPermission(user)
+                               || !source.hasWritePermission(user))
+                       throw new InsufficientPermissionsException("You don't have the " +
+                                       "necessary permissions");
+               // Use the same timestamp for all subsequent modifications to make
+               // changes appear simultaneous.
+               Date now = new Date();
+               // If source and destination are not in the same user's namespace,
+               // change owners and check quota.
+               if (!sourceOwner.equals(destinationOwner)) {
+                       changeOwner(source, destinationOwner, user, now);
+                       if (getQuotaLeft(destinationOwner.getId()) < 0)
+                               throw new QuotaExceededException("Not enough free space " +
+                                               "available in destination folder");
+               }
+               // Perform the move.
+               Folder oldParent = source.getParent();
+               oldParent.removeSubfolder(source);
+               destination.addSubfolder(source);
+               // Mark the former parent and destination trees upwards as modified.
+               touchParentFolders(oldParent, user, now);
+               touchParentFolders(source, user, now);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getDeletedFiles(java.lang.Long)
+       /**
+        * Recursively change the owner of the specified folder and all of its
+        * contents to the specified owner. Also mark them all as modified with the
+        * specified modifier and modificationDate.
         */
-       public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
+       private void changeOwner(Folder folder, User owner, User modifier, Date modificationDate) {
+               for (FileHeader file: folder.getFiles()) {
+                       file.setOwner(owner);
+                       file.getAuditInfo().setModificationDate(modificationDate);
+                       file.getAuditInfo().setModifiedBy(modifier);
+               }
+               for (Folder sub: folder.getSubfolders())
+                       changeOwner(sub, owner, modifier, modificationDate);
+               folder.setOwner(owner);
+               folder.getAuditInfo().setModificationDate(modificationDate);
+               folder.getAuditInfo().setModifiedBy(modifier);
+       }
+
+       @Override
+       public List<FileHeader> getDeletedFiles(Long userId) throws ObjectNotFoundException {
                // Validate.
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
 
                // Do the actual work.
-               final List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
                final List<FileHeader> files = dao.getDeletedFiles(userId);
-               for (final FileHeader f : files)
-                       result.add(f.getDTO());
-               return result;
+               return files;
        }
 
        @Override
@@ -1156,32 +1310,39 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if (parent == null)
                        throw new ObjectNotFoundException("The specified file has no parent folder");
                User user = dao.getEntityById(User.class, userId);
-               if (!file.hasDeletePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getUsername() +
-                                               " cannot restore file " + file.getName());
-
-               file.setDeleted(false);
-               dao.update(file);
+        untrashFile(user, file);
+               touchParentFolders(parent, user, new Date());
        }
 
+    private void untrashFile(User user, FileHeader file) throws InsufficientPermissionsException {
+        if (!file.hasDeletePermission(user))
+            throw new InsufficientPermissionsException("User " + user.getUsername() +
+                        " cannot restore file " + file.getName());
+
+        file.setDeleted(false);
+    }
+
        @Override
        public void moveFolderToTrash(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               Folder folder = dao.getEntityById(Folder.class, folderId);
-               User user = dao.getEntityById(User.class, userId);
-               if (!folder.hasDeletePermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               folder.setDeleted(true);
-               dao.update(folder);
-               for (FileHeader file : folder.getFiles())
-                       moveFileToTrash(userId, file.getId());
-               for (Folder subFolder : folder.getSubfolders())
-                       moveFolderToTrash(userId, subFolder.getId());
-
-       }
+        if (userId == null)
+            throw new ObjectNotFoundException("No user specified");
+        if (folderId == null)
+            throw new ObjectNotFoundException("No folder specified");
+        Folder folder = dao.getEntityById(Folder.class, folderId);
+        User user = dao.getEntityById(User.class, userId);
+        trashFolder(user, folder);
+        touchParentFolders(folder, user, new Date());
+       }
+
+    private void trashFolder(User user, Folder folder) throws ObjectNotFoundException, InsufficientPermissionsException {
+        if (!folder.hasDeletePermission(user))
+            throw new InsufficientPermissionsException("You don't have the necessary permissions");
+        folder.setDeleted(true);
+        for (FileHeader file : folder.getFiles())
+            trashFile(user, file);
+        for (Folder subFolder : folder.getSubfolders())
+            trashFolder(user, subFolder);
+    }
 
        @Override
        public void removeFolderFromTrash(Long userId, Long folderId)
@@ -1192,48 +1353,50 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No folder specified");
                Folder folder = dao.getEntityById(Folder.class, folderId);
                User user = dao.getEntityById(User.class, userId);
-               if (!folder.hasDeletePermission(user))
-                       throw new InsufficientPermissionsException("User " + user.getUsername() +
-                                               " cannot restore folder " + folder.getName());
-               folder.setDeleted(false);
-               for (FileHeader file : folder.getFiles())
-                       removeFileFromTrash(userId, file.getId());
-               for (Folder subFolder : folder.getSubfolders())
-                       removeFolderFromTrash(userId, subFolder.getId());
-               dao.update(folder);
+        untrashFolder(user, folder);
+               touchParentFolders(folder, user, new Date());
        }
 
+    private void untrashFolder(User user, Folder folder) throws ObjectNotFoundException, InsufficientPermissionsException {
+        if (!folder.hasDeletePermission(user))
+            throw new InsufficientPermissionsException("User " + user.getUsername() +
+                        " cannot restore folder " + folder.getName());
+        folder.setDeleted(false);
+        for (FileHeader file : folder.getFiles())
+            untrashFile(user, file);
+        for (Folder subFolder : folder.getSubfolders())
+            untrashFolder(user, subFolder);
+    }
+
        @Override
-       public List<FolderDTO> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
+       public List<Folder> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
                List<Folder> folders = dao.getDeletedRootFolders(userId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
-               for (Folder folder : folders)
-                       result.add(folder.getDTO());
-               return result;
+               return folders;
        }
 
        @Override
        public void emptyTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
-               for (FolderDTO fdto : deletedRootFolders)
-                       deleteFolder(userId, fdto.getId());
-               List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
-               for (FileHeaderDTO filedto : deletedFiles)
-                       deleteFile(userId, filedto.getId());
+               List<Folder> deletedRootFolders = getDeletedRootFolders(userId);
+               for (Folder folder : deletedRootFolders)
+                       deleteFolder(userId, folder.getId());
+               List<FileHeader> deletedFiles = getDeletedFiles(userId);
+               for (FileHeader file : deletedFiles)
+                       deleteFile(userId, file.getId());
        }
 
        @Override
        public void restoreTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
-               for (FolderDTO fdto : deletedRootFolders)
-                       removeFolderFromTrash(userId, fdto.getId());
-               List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
-               for (FileHeaderDTO filedto : deletedFiles)
-                       removeFileFromTrash(userId, filedto.getId());
+               List<Folder> deletedRootFolders = getDeletedRootFolders(userId);
+               for (Folder folder : deletedRootFolders)
+                       removeFolderFromTrash(userId, folder.getId());
+               List<FileHeader> deletedFiles = getDeletedFiles(userId);
+               for (FileHeader file : deletedFiles)
+                       removeFileFromTrash(userId, file.getId());
        }
 
        @Override
-       public User createUser(String username, String name, String mail) throws ObjectNotFoundException {
+       public User createUser(String username, String name, String mail,
+                               String idp, String idpid, String homeOrg) throws ObjectNotFoundException {
                if (username == null)
                        throw new ObjectNotFoundException("No username specified");
                if (name == null)
@@ -1243,12 +1406,18 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                user.setUsername(username);
                user.setName(name);
                user.setEmail(mail);
+               user.setIdentityProvider(idp);
+               user.setIdentityProviderId(idpid);
                Date now = new Date();
                AuditInfo auditInfo = new AuditInfo();
                auditInfo.setCreationDate(now);
                auditInfo.setModificationDate(now);
                user.setAuditInfo(auditInfo);
+               user.setActive(true);
                user.generateAuthToken();
+               user.generateWebDAVPassword();
+               user.setUserClass(getDefaultUserClass());
+        user.setHomeOrganization(homeOrg);
                dao.create(user);
                // Make sure we get an ID in the user object.
                dao.flush();
@@ -1257,6 +1426,29 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return user;
        }
 
+       /**
+        * Get the default user class, which is the one with the lowest quota.
+        */
+       private UserClass getDefaultUserClass() {
+               return getUserClasses().get(0);
+       }
+
+       @Override
+       public List<UserClass> getUserClasses() {
+               List<UserClass> classes = dao.getUserClasses();
+               // Create a default user class for first-time use. Afterwards, the
+               // admin should modify or add to the userclass table.
+               if (classes.size() == 0) {
+                       UserClass defaultClass = new UserClass();
+                       defaultClass.setName("default");
+                       Long defaultQuota = getConfiguration().getLong("quota", new Long(52428800L));
+                       defaultClass.setQuota(defaultQuota);
+                       dao.create(defaultClass);
+                       classes.add(defaultClass);
+               }
+               return classes;
+       }
+
        @Override
        public User findUserByEmail(String email) {
                return dao.findUserByEmail(email);
@@ -1268,17 +1460,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public User updateUser(String username, String name, String mail) throws ObjectNotFoundException {
-               if (username == null)
-                       throw new ObjectNotFoundException("No username specified");
-
-               User user = dao.getUser(username);
-               user.setName(name);
-               user.setEmail(mail);
-               return user;
-       }
-
-       @Override
        public User findUser(String username) {
                if (username == null)
                        return null;
@@ -1294,11 +1475,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return user;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolderPermissions(java.lang.Long, java.lang.Long)
-        */
        @Override
-       public Set<PermissionDTO> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
+       public Set<Permission> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
@@ -1308,56 +1486,72 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if(!folder.hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
                Set<Permission> perms = folder.getPermissions();
-               Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
+               Set<Permission> result = new LinkedHashSet<Permission>();
                for (Permission perm : perms)
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
-                               result.add(perm.getDTO());
+                               result.add(perm);
                for (Permission perm : perms)
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
                        } else
-                               result.add(perm.getDTO());
+                               result.add(perm);
                return result;
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#setFolderPermissions(java.lang.Long, java.lang.Long, java.util.Set)
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * folder.
+        *
+        * @param user
+        * @param folder
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
         */
-       @Override
-       public void setFolderPermissions(Long userId, Long folderId, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               User user = dao.getEntityById(User.class, userId);
-               Folder folder = dao.getEntityById(Folder.class, folderId);
-               if(!folder.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               folder.getPermissions().clear();
-               for (PermissionDTO dto : permissions) {
-                       if (dto.getUser()!=null && dto.getUser().getId().equals(folder.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
-                                       throw new InsufficientPermissionsException("Can't remove permissions from owner");
-                       folder.addPermission(getPermission(dto));
+       private void setFolderPermissions(User user, Folder folder, Set<Permission> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
+               if (permissions != null && !permissions.isEmpty()) {
+                       User owner = folder.getOwner();
+                       Permission ownerPerm = null;
+                       for (Permission perm : permissions)
+                               if (perm.getUser() != null && perm.getUser().getId().equals(owner.getId())) {
+                                       ownerPerm = perm;
+                                       break;
+                               }
+                       if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
+                               throw new InsufficientPermissionsException("Can't remove permissions from owner");
+                       // Delete previous entries
+                       for (Permission perm: folder.getPermissions())
+                               dao.delete(perm);
+                       folder.getPermissions().clear();
+                       for (Permission p : permissions) {
+                               // Skip 'empty' permission entries.
+                               if (!p.getRead() && !p.getWrite() && !p.getModifyACL()) continue;
+                               folder.addPermission(getPermission(p));
+                       }
+                       dao.update(folder);
+                       for (FileHeader file : folder.getFiles()) {
+                               setFilePermissions(file, permissions);
+                               Date now = new Date();
+                               file.getAuditInfo().setModificationDate(now);
+                               file.getAuditInfo().setModifiedBy(user);
+                       }
+                       for (Folder sub : folder.getSubfolders())
+                               setFolderPermissions(user, sub, permissions);
                }
-               dao.update(folder);
-               for (FileHeader fh : folder.getFiles())
-                       setFilePermissions(userId, fh.getId(), fh.isReadForAll(), permissions);
-               for (Folder sub : folder.getSubfolders())
-                       setFolderPermissions(userId, sub.getId(), permissions);
        }
 
-       private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
+       private Permission getPermission(Permission perm) throws ObjectNotFoundException {
                Permission res = new Permission();
-               if (dto.getGroup() != null)
-                       res.setGroup(dao.getEntityById(Group.class, dto.getGroup().getId()));
-               else if (dto.getUser() != null)
-                       if (dto.getUser().getId() == null)
-                               res.setUser(dao.getUser(dto.getUser().getUsername()));
+               if (perm.getGroup() != null)
+                       res.setGroup(dao.getEntityById(Group.class, perm.getGroup().getId()));
+               else if (perm.getUser() != null)
+                       if (perm.getUser().getId() == null)
+                               res.setUser(dao.getUser(perm.getUser().getUsername()));
                        else
-                               res.setUser(dao.getEntityById(User.class, dto.getUser().getId()));
-               res.setRead(dto.hasRead());
-               res.setWrite(dto.hasWrite());
-               res.setModifyACL(dto.hasModifyACL());
+                               res.setUser(dao.getEntityById(User.class, perm.getUser().getId()));
+               res.setRead(perm.hasRead());
+               res.setWrite(perm.hasWrite());
+               res.setModifyACL(perm.hasModifyACL());
                return res;
        }
 
@@ -1365,18 +1559,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
         */
        @Override
-       public List<UserDTO> getUsersByUserNameLike(String username) {
+       public List<User> getUsersByUserNameLike(String username) {
                List<User> users = dao.getUsersByUserNameLike(username);
-               List<UserDTO> result = new ArrayList<UserDTO>();
-               for (User u : users)
-                       result.add(u.getDTO());
-               return result;
+               return users;
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#addUserToGroup(java.lang.Long, java.lang.Long, java.lang.Long)
-        */
        @Override
        public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
                if (userId == null)
@@ -1407,22 +1595,19 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public List<FolderDTO> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
+       public List<Folder> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                List<Folder> folders = dao.getSharedRootFolders(userId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
+               List<Folder> result = new ArrayList<Folder>();
                for (Folder f : folders) {
-                       FolderDTO dto = f.getDTO();
-                       dto.setSubfolders(getSharedSubfolders(userId, f.getId()));
-                       result.add(dto);
+                       Folder lf = f;
+                       lf.setSubfolders(getSharedSubfolders(userId, f.getId()));
+                       result.add(lf);
                }
                return result;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#removeMemberFromGroup(java.lang.Long, java.lang.Long, java.lang.Long)
-        */
        @Override
        public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
@@ -1441,27 +1626,21 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersSharingFoldersForUser(java.lang.Long)
-        */
        @Override
-       public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
+       public List<User> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
                List<User> users = dao.getUsersSharingFoldersForUser(userId);
                List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
-               List<UserDTO> res = new ArrayList<UserDTO>();
+               List<User> result = new ArrayList<User>();
                for (User u : users)
-                       res.add(u.getDTO());
+                       result.add(u);
                for(User fu : usersFiles)
                        if(!users.contains(fu))
-                               res.add(fu.getDTO());
-               return res;
+                               result.add(fu);
+               return result;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getFilePermissions(java.lang.Long, java.lang.Long)
-        */
        @Override
-       public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
+       public Set<Permission> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -1471,134 +1650,121 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if(!folder.hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
                Set<Permission> perms = folder.getPermissions();
-               Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
+               Set<Permission> result = new LinkedHashSet<Permission>();
                for (Permission perm : perms)
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
-                               result.add(perm.getDTO());
+                               result.add(perm);
                for (Permission perm : perms)
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
                        } else
-                               result.add(perm.getDTO());
+                               result.add(perm);
                return result;
        }
 
-       @Override
-       public void setFilePermissions(Long userId, Long fileId, Boolean readForAll, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-               if(!file.hasModifyACLPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-
-               if (readForAll != null)
-                       if (user.equals(file.getOwner()))
-                               file.setReadForAll(readForAll);
-                       else
-                               throw new InsufficientPermissionsException("Only the owner can change the read-for-all flag");
-
+       /**
+        * Set the provided permissions as the new permissions of the specified
+        * file. This method sets the modification date/user attributes to the
+        * current values as a side effect.
+        *
+        * @param file
+        * @param permissions
+        * @throws ObjectNotFoundException
+        * @throws InsufficientPermissionsException
+        */
+       private void setFilePermissions(FileHeader file,
+                               Set<Permission> permissions)
+                       throws ObjectNotFoundException, InsufficientPermissionsException {
                if (permissions != null && !permissions.isEmpty()) {
+                       Permission ownerPerm = null;
+                       for (Permission perm : permissions)
+                               if (perm.getUser() != null && perm.getUser().getId().equals(file.getOwner().getId())) {
+                                       ownerPerm = perm;
+                                       break;
+                               }
+                       if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
+                               throw new InsufficientPermissionsException("Can't remove permissions from owner");
+                       // Delete previous entries.
+                       for (Permission perm: file.getPermissions())
+                               dao.delete(perm);
                        file.getPermissions().clear();
-                       for (PermissionDTO dto : permissions) {
-                               if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
-                                       throw new InsufficientPermissionsException("Can't remove permissions from owner");
-                               file.addPermission(getPermission(dto));
+                       for (Permission perm : permissions) {
+                               // Skip 'empty' permission entries.
+                               if (!perm.getRead() && !perm.getWrite() && !perm.getModifyACL()) continue;
+                               file.addPermission(getPermission(perm));
                        }
+                       dao.flush();
                }
-
-               // Update the file if there was a change.
-               if (readForAll != null || permissions != null && !permissions.isEmpty())
-                       dao.update(file);
-
        }
 
        @Override
-       public List<FileHeaderDTO> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
+       public List<FileHeader> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
-               List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
-               for (FileHeader f : files)
-                       result.add(f.getDTO());
-               return result;
+               return files;
        }
 
        @Override
-       public List<FileHeaderDTO> getSharedFiles(Long userId) throws ObjectNotFoundException {
+       public List<FileHeader> getSharedFiles(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                List<FileHeader> files = dao.getSharedFiles(userId);
-               List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
-               for (FileHeader f : files)
-                       result.add(f.getDTO());
-               return result;
+               return files;
        }
 
        @Override
-       public List<FolderDTO> getSharedFolders(Long userId) throws ObjectNotFoundException {
+       public List<Folder> getSharedFolders(Long userId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                List<Folder> folders = dao.getSharedFolders(userId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
-               for (Folder f : folders)
-                       result.add(f.getDTO());
-               return result;
+               return folders;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getSharedFiles(java.lang.Long, java.lang.Long)
-        */
        @Override
-       public List<FileHeaderDTO> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
+       public List<FileHeader> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
                if (ownerId == null)
                        throw new ObjectNotFoundException("No owner specified");
                if (callingUserId == null)
                        throw new ObjectNotFoundException("No calling user specified");
                List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
-               List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
-               for (FileHeader f : folders)
-                       result.add(f.getDTO());
-               return result;
+               return folders;
        }
 
        @Override
-       public List<FolderDTO> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
+       public List<Folder> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
                if (ownerId == null)
                        throw new ObjectNotFoundException("No owner specified");
                if (callingUserId == null)
                        throw new ObjectNotFoundException("No calling user specified");
                List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
+               List<Folder> result = new ArrayList<Folder>();
                for (Folder f : folders) {
-                       FolderDTO dto = f.getDTO();
-                       dto.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
-                       result.add(dto);
+                       Folder lf = f;
+                       lf.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
+                       result.add(lf);
                }
                return result;
 
        }
 
        @Override
-       public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
+       public List<Folder> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (folderId == null)
                        throw new ObjectNotFoundException("No folder specified");
                User user = dao.getEntityById(User.class, userId);
                Folder folder = dao.getEntityById(Folder.class, folderId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
-               if (folder.isShared(user))
+               List<Folder> result = new ArrayList<Folder>();
+               if (folder.isShared(user) || folder.isReadForAll())
                        for (Folder f : folder.getSubfolders())
-                               if (f.isShared(user) && !f.isDeleted())
-                                       result.add(f.getDTO());
+                               if ((f.isShared(user) || f.isReadForAll()) && !f.isDeleted())
+                                       result.add(f);
                return result;
        }
 
        @Override
-       public List<FolderDTO> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
+       public List<Folder> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (callingUserId == null)
@@ -1607,127 +1773,96 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                        throw new ObjectNotFoundException("No folder specified");
                User user = dao.getEntityById(User.class, callingUserId);
                Folder folder = dao.getEntityById(Folder.class, folderId);
-               List<FolderDTO> result = new ArrayList<FolderDTO>();
+               List<Folder> result = new ArrayList<Folder>();
                if (folder.isSharedForOtherUser(user))
                        for (Folder f : folder.getSubfolders())
                                if (f.isSharedForOtherUser(user) && !f.isDeleted()){
-                                       FolderDTO dto = f.getDTO();
-                                       dto.setSubfolders(getSharedSubfolders(userId, callingUserId, dto.getId()));
-                                       result.add(dto);
+                                       Folder lf = f;
+                                       lf.setSubfolders(getSharedSubfolders(userId, callingUserId, lf.getId()));
+                                       result.add(lf);
                                }
                return result;
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#searchFiles(java.lang.Long, java.lang.String)
-        */
        @Override
-       public List<FileHeaderDTO> searchFiles(Long userId, String query) throws ObjectNotFoundException {
+       public List<FileHeader> searchFiles(Long userId, String query) throws ObjectNotFoundException {
+        long startTime = System.currentTimeMillis();
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                User user = getUser(userId);
                if (query == null)
                        throw new ObjectNotFoundException("No query specified");
                List<FileHeader> files = search(user.getId(), query);
-               List<FileHeaderDTO> res = new ArrayList<FileHeaderDTO>();
-               for(FileHeader f : files)
-                       res.add(f.getDTO());
-               return res;
+               
+        long stopTime = System.currentTimeMillis();
+        logger.info("Total time: " + (stopTime - startTime));
+               return files;
        }
 
        /**
         * Performs the actuals search on the solr server and returns the results
         *
-        * We have to use the dismax query type (instead of the
-        * standard) because it allows for search time field boosting. This is because we can't use indexing
-        * time field boosting due to the patched rich indexing API that does not allow it
-        *
         * @param userId
         * @param query
         * @return a List of FileHeader objects
         */
        private List<FileHeader> search(Long userId, String query) {
+        final int maxRows = 100;
+               List<FileHeader> result = new ArrayList<FileHeader>();
                try {
-                       HttpClient httpClient = new HttpClient();
-
-                       GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl"));
-                       NameValuePair[] params = {new NameValuePair("qt", "dismax"),
-                                                                               new NameValuePair("q", query),
-                                                                               new NameValuePair("sort", "score desc"),
-                                                                               new NameValuePair("indent", "on")};
-                       method.setQueryString(params);
-                       int retryCount = 0;
-                       int statusCode = 0;
-                       String response = null;
-                       do {
-                               statusCode = httpClient.executeMethod(method);
-                               logger.debug("HTTP status: " + statusCode);
-                               response = method.getResponseBodyAsString();
-                               logger.debug(response);
-                               retryCount++;
-                               if (statusCode != 200 && retryCount < 3)
-                                       try {
-                                               Thread.sleep(3000); //Give Solr a little time to be available
-                                       } catch (InterruptedException e) {
-                                       }
-                       } while (statusCode != 200 && retryCount < 3);
-                       if (statusCode != 200)
-                               throw new EJBException("Search query return error:\n" + response);
-
-                       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-                       DocumentBuilder db = dbf.newDocumentBuilder();
-                       Document doc = db.parse(method.getResponseBodyAsStream());
-                       method.releaseConnection();
-
-                       Node root = doc.getElementsByTagName("response").item(0);
-                       Node lst = root.getFirstChild().getNextSibling();
-                       Node status = lst.getFirstChild().getNextSibling();
-                       if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") &&
-                               status.getTextContent().equals("0")) {
-                               List<FileHeader> fileResult = new ArrayList<FileHeader>();
-                               Node result = lst.getNextSibling().getNextSibling();
-                               NodeList docs = result.getChildNodes();
-                               User user = getUser(userId);
-                               for (int i=1; i<docs.getLength(); i=i+2) {
-                                       Node d = docs.item(i);
-                                       NodeList docData = d.getChildNodes();
-                                       for (int j=1; j<docData.getLength(); j=j+2) {
-                                               Node dd = docData.item(j);
-                                               if (dd.getAttributes().item(0).getNodeName().equals("name") &&
-                                                       dd.getAttributes().item(0).getNodeValue().equals("id")) {
-                                                       Long fileId = Long.valueOf(dd.getTextContent());
-                                                       try {
-                                                               FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-                                                               if (file.hasReadPermission(user)) {
-                                                                       fileResult.add(file);
-                                                                       logger.debug("File added " + fileId);
-                                                               }
-                                                       } catch (ObjectNotFoundException e) {
-                                                               logger.warn("Search result not found", e);
-                                                       }
-                                               }
-                                       }
+                       CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
+            List<Group> groups = dao.getGroupsContainingUser(userId);
+            String constructedQuery = escapeCharacters(normalizeSearchQuery(query)) + " AND (public: true OR ureaders: " + userId;
+            if (!groups.isEmpty()) {
+                constructedQuery += " OR (";
+                for (int i=0; i<groups.size(); i++) {
+                    Group g = groups.get(i);
+                    constructedQuery += "greaders :" + g.getId();
+                    if (i < groups.size() - 1)
+                        constructedQuery += " OR ";
+                }
+                constructedQuery += ")";
+            }
+            constructedQuery += ")";
+                       SolrQuery solrQuery = new SolrQuery(constructedQuery);
+            solrQuery.setRows(maxRows);
+            long startTime = System.currentTimeMillis();
+                       QueryResponse response = solr.query(solrQuery);
+                       SolrDocumentList results = response.getResults();
+            if (results.getNumFound() > maxRows) {
+                solrQuery.setRows(Integer.valueOf((int) results.getNumFound()));
+                response = solr.query(solrQuery);
+                results = response.getResults();
+            }
+            long stopTime = System.currentTimeMillis();
+            logger.info("Search time:" +  (stopTime - startTime));
+                       User user = getUser(userId);
+            startTime = System.currentTimeMillis();
+                       for (SolrDocument d : results) {
+                               Long id = Long.valueOf((String) d.getFieldValue("id"));
+                               try {
+                                       FileHeader f = dao.getEntityById(FileHeader.class, id);
+                                       result.add(f);
+                               } catch (ObjectNotFoundException e) {
+                                       logger.warn("Search result id " + id + " cannot be found", e);
                                }
-                               return fileResult;
                        }
-                       throw new EJBException();
-               } catch (HttpException e) {
-                       throw new EJBException(e);
-               } catch (IOException e) {
-                       throw new EJBException(e);
-               } catch (SAXException e) {
+            stopTime = System.currentTimeMillis();
+            logger.info("File loads: " + (stopTime - startTime));
+               } catch (MalformedURLException e) {
+                       logger.error(e);
                        throw new EJBException(e);
-               } catch (ParserConfigurationException e) {
+               } catch (SolrServerException e) {
+                       logger.error(e);
                        throw new EJBException(e);
                } catch (ObjectNotFoundException e) {
+                       logger.error(e);
                        throw new EJBException(e);
                }
+               return result;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#copyFiles(java.lang.Long, java.util.List, java.lang.Long)
-        */
        @Override
        public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
                for(Long l : fileIds){
@@ -1738,11 +1873,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFiles(java.lang.Long, java.util.List, java.lang.Long)
-        */
        @Override
-       public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException, GSSIOException, QuotaExceededException {
+       public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
                for(Long l : fileIds){
                        FileHeader file = dao.getEntityById(FileHeader.class, l);
                        moveFile(userId, l, destId, file.getName());
@@ -1750,60 +1882,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFiles(java.lang.Long, java.util.List)
-        */
-       @Override
-       public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               final User user = dao.getEntityById(User.class, userId);
-               List<File> filesToRemove = new ArrayList<File>();
-               //first delete database objects
-               for(Long fileId : fileIds){
-                       if (fileId == null)
-                               throw new ObjectNotFoundException("No file specified");
-                       final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
-                       final Folder parent = file.getFolder();
-                       if (parent == null)
-                               throw new ObjectNotFoundException("The specified file has no parent folder");
-                       if (!file.hasDeletePermission(user))
-                               throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
-
-                       parent.removeFile(file);
-                       for (final FileBody body : file.getBodies()) {
-                               final File fileContents = new File(body.getStoredFilePath());
-                               filesToRemove.add(fileContents);
-                       }
-                       dao.delete(file);
-               }
-               //then remove physical files if everything is ok
-               for(File physicalFile : filesToRemove)
-                       if (!physicalFile.delete())
-                               logger.error("Could not delete file " + physicalFile.getPath());
-               //then unindex deleted files
-               for(Long fileId : fileIds)
-                       indexFile(fileId, true);
-
-       }
-
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFilesToTrash(java.lang.Long, java.util.List)
-        */
-       @Override
-       public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
-               for(Long l : fileIds)
-                       moveFileToTrash(userId, l);
-
-       }
-
-       @Override
-       public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
-               for(Long l : fileIds)
-                       removeFileFromTrash(userId, l);
-
-       }
-
        @Override
        public Nonce createNonce(Long userId) throws ObjectNotFoundException {
                if (userId == null)
@@ -1840,9 +1918,6 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                user.setNonceExpiryDate(nonceExpiryDate);
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserStatistics(java.lang.Long)
-        */
        @Override
        public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
                if (userId == null)
@@ -1851,63 +1926,12 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                stats.setFileCount(dao.getFileCount(userId));
                Long fileSize = dao.getFileSize(userId);
                stats.setFileSize(fileSize);
-               Long quota = getConfiguration().getLong("quota", new Long(52428800L));
+               Long quota = getQuota(userId);
                Long quotaLeft = quota - fileSize;
                stats.setQuotaLeftSize(quotaLeft);
                return stats;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#getVersions(java.lang.Long, java.lang.Long)
-        */
-       @Override
-       public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No file specified");
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader header = dao.getEntityById(FileHeader.class, fileId);
-               if(!header.hasReadPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
-               for(int i = header.getBodies().size()-1 ; i>=0; i--)
-                       result.add(header.getBodies().get(i).getDTO());
-               return result;
-       }
-
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#removeVersion(java.lang.Long, java.lang.Long, java.lang.Long)
-        */
-       @Override
-       public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No file specified");
-               if (bodyId == null)
-                       throw new ObjectNotFoundException("No body specified");
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader header = dao.getEntityById(FileHeader.class, fileId);
-               if(!header.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               FileBody body = dao.getEntityById(FileBody.class, bodyId);
-               if(body.equals(header.getCurrentBody())){
-
-                       if(header.getBodies().size() == 1)
-                               throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
-                       for(FileBody b : header.getBodies())
-                               if(b.getVersion() == body.getVersion()-1)
-                                       header.setCurrentBody(b);
-               }
-               final File fileContents = new File(body.getStoredFilePath());
-               if (!fileContents.delete())
-                       logger.error("Could not delete file " + body.getStoredFilePath());
-               header.getBodies().remove(body);
-
-
-       }
-
        @Override
        public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
                if (userId == null)
@@ -1946,180 +1970,121 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                while(it.hasNext()){
                        FileBody body = it.next();
                        if(!body.equals(header.getCurrentBody())){
-                               final File fileContents = new File(body.getStoredFilePath());
-                               if (!fileContents.delete())
-                                       logger.error("Could not delete file " + body.getStoredFilePath());
+                               deleteActualFile(body.getStoredFilePath());
                                it.remove();
                                dao.delete(body);
                        }
                }
                header.getCurrentBody().setVersion(1);
 
+               Folder parent = header.getFolder();
+               touchParentFolders(parent, user, new Date());
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#toggleFileVersioning(java.lang.Long, java.lang.Long, boolean)
+       /**
+        * Gets the quota left for specified user ID.
         */
-       @Override
-       public void toggleFileVersioning(Long userId, Long fileId, boolean versioned) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (fileId == null)
-                       throw new ObjectNotFoundException("No file specified");
-               User user = dao.getEntityById(User.class, userId);
-               FileHeader header = dao.getEntityById(FileHeader.class, fileId);
-               if(!header.hasWritePermission(user))
-                       throw new InsufficientPermissionsException("You don't have the necessary permissions");
-               if(!header.isVersioned() == versioned){
-                       if(header.isVersioned())
-                               removeOldVersions(userId, fileId);
-                       header.setVersioned(versioned);
-
-               }
+       private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
+               Long fileSize = dao.getFileSize(userId);
+               Long quota = getQuota(userId);
+               return quota - fileSize;
        }
 
        /**
-        * Gets the quota left for specified userId
-        * @param userId
-        * @return
+        * Gets the quota for specified user ID.
         */
-       private Long getQuotaLeft(Long userId){
-               Long fileSize = dao.getFileSize(userId);
-               Long quota = getConfiguration().getLong("quota", new Long(52428800L));
-               return quota - fileSize;
+       private Long getQuota(Long userId) throws ObjectNotFoundException{
+               UserClass uc = getUser(userId).getUserClass();
+               if (uc == null)
+                       uc = getDefaultUserClass();
+               return uc.getQuota();
        }
 
-       public void rebuildSolrIndex() {
-               MessageProducer sender = null;
-               Session session = null;
-               Connection qConn = null;
+       @Override
+    @TransactionAttribute(TransactionAttributeType.NEVER)
+       public String rebuildSolrIndex() {
                try {
-                       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-                       DocumentBuilder db = dbf.newDocumentBuilder();
-                       Document doc = db.newDocument();
-                       Node root = doc.createElement("delete");
-                       doc.appendChild(root);
-                       Node queryNode = doc.createElement("query");
-                       root.appendChild(queryNode);
-                       queryNode.appendChild(doc.createTextNode("*:*"));
-
-                       TransformerFactory fact = TransformerFactory.newInstance();
-                       Transformer trans = fact.newTransformer();
-                       trans.setOutputProperty(OutputKeys.INDENT, "yes");
-                       StringWriter sw = new StringWriter();
-                       StreamResult sr = new StreamResult(sw);
-                       DOMSource source = new DOMSource(doc);
-                       trans.transform(source, sr);
-                       logger.debug(sw.toString());
-
-                       HttpClient httpClient = new HttpClient();
-                       PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
-                       method.setRequestEntity(new StringRequestEntity(sw.toString()));
-                       int retryCount = 0;
-                       int statusCode = 0;
-                       String response = null;
-                       do {
-                               statusCode = httpClient.executeMethod(method);
-                               logger.debug("HTTP status: " + statusCode);
-                               response = method.getResponseBodyAsString();
-                               logger.debug(response);
-                               retryCount++;
-                               if (statusCode != 200 && retryCount < 3)
-                                       try {
-                                               Thread.sleep(10000); //Give Solr a little time to be available
-                                       } catch (InterruptedException e) {
-                                       }
-                       } while (statusCode != 200 && retryCount < 3);
-                       method.releaseConnection();
-                       if (statusCode != 200)
-                               throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response);
-                       List<Long> fileIds = dao.getAllFileIds();
-
-                       Context jndiCtx = new InitialContext();
-                       ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
-                       Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
-                       qConn = factory.createConnection();
-                       session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
-                       sender = session.createProducer(queue);
+            CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
+                       solr.deleteByQuery("*:*");
+                       solr.commit();
+            logger.info("Deleted everything in solr");
 
+                       List<Long> fileIds = dao.getAllFileIds();
+            logger.info("Total of " + fileIds.size() + " will be indexed");
+            int i = 0;
                        for (Long id : fileIds) {
-                               MapMessage map = session.createMapMessage();
-                               map.setObject("id", id);
-                               map.setBoolean("delete", false);
-                               sender.send(map);
+                try {
+                    postFileToSolr(solr, id);
+                }
+                catch (ObjectNotFoundException e) {
+                    logger.error("Indexing of file id " + id + " failed.", e);
+                }
+                i++;
+                if (i % 10 == 0) {
+                    solr.commit();
+                    logger.info("Sent commit to solr at file " + i);
+                }
                        }
-                       sendOptimize(httpClient, 0);
-               } catch (DOMException e) {
-                       throw new EJBException(e);
-               } catch (TransformerConfigurationException e) {
-                       throw new EJBException(e);
-               } catch (IllegalArgumentException e) {
-                       throw new EJBException(e);
-               } catch (HttpException e) {
-                       throw new EJBException(e);
-               } catch (UnsupportedEncodingException e) {
-                       throw new EJBException(e);
-               } catch (ParserConfigurationException e) {
-                       throw new EJBException(e);
-               } catch (TransformerException e) {
-                       throw new EJBException(e);
+                       solr.optimize();
+                       solr.commit();
+            logger.info("Finished indexing of " + i + " files");
+            return "Finished indexing of " + i + " files";
                } catch (IOException e) {
                        throw new EJBException(e);
-               } catch (NamingException e) {
-                       throw new EJBException(e);
-               } catch (JMSException e) {
+               } catch (SolrServerException e) {
                        throw new EJBException(e);
                }
-               finally {
-                       try {
-                               if (sender != null)
-                                       sender.close();
-                               if (session != null)
-                                       session.close();
-                               if (qConn != null)
-                                       qConn.close();
-                       }
-                       catch (JMSException e) {
-                               logger.warn(e);
-                       }
-               }
        }
 
-       /**
-        * Sends a optimize message to the solr server
-        *
-        * @param httpClient
-        * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive
-        *                                      calls to stop the recursion
-        * @throws UnsupportedEncodingException
-        * @throws IOException
-        * @throws HttpException
-        */
-       private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException {
-               PostMethod method = null;
+       @Override
+    @TransactionAttribute(TransactionAttributeType.NEVER)
+       public String refreshSolrIndex() {
                try {
-                       logger.debug("Optimize retry: " + retryCount);
-                       method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
-                       method.setRequestEntity(new StringRequestEntity("<optimize/>", "text/xml", "iso8859-1"));
-                       int statusCode = httpClient.executeMethod(method);
-                       logger.debug("HTTP status: " + statusCode);
-                       String response = method.getResponseBodyAsString();
-                       logger.debug(response);
-                       if (statusCode != 200 && retryCount < 2) {
-                               try {
-                                       Thread.sleep(10000); //Give Solr a little time to be available
-                               } catch (InterruptedException e) {
-                               }
-                               sendOptimize(httpClient, retryCount + 1);
+                       CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
+                       
+                       List<Long> fileIds = dao.getAllFileIds();
+            logger.info("Total of " + fileIds.size() + " will be checked");
+            int i = 0;
+                       for (Long id : fileIds) {
+                if (!fileIsInSolr(solr, id)) {
+                    try {
+                        postFileToSolr(solr, id);
+                    }
+                    catch (ObjectNotFoundException e) {
+                       logger.error("Indexing of file id " + id + " failed.", e);
+                    }
+                }
+                i++;
+                if (i % 10 == 0) {
+                    solr.commit();
+                    logger.info("Sent commit to solr at file " + i);
+                }
                        }
-               }
-               finally {
-                       if (method != null)
-                               method.releaseConnection();
+                       solr.optimize();
+                       solr.commit();
+            logger.info("Finished indexing of " + i + " files");
+            return "Finished indexing of " + i + " files";
+               } catch (IOException e) {
+                       throw new EJBException(e);
+               } catch (SolrServerException e) {
+                       throw new EJBException(e);
                }
        }
 
-       public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, File fileObject)
+    private boolean fileIsInSolr(CommonsHttpSolrServer solr, Long id) {
+        try {
+            SolrQuery query = new SolrQuery("id:" + id);
+            QueryResponse response = solr.query(query);
+            return !(response.getResults().size() == 0);
+        }
+        catch (SolrServerException e) {
+            logger.warn("Exception while checking file " + id, e);
+            return false;
+        }
+    }
+
+    @Override
+       public FileHeader createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
                        InsufficientPermissionsException, QuotaExceededException {
                // Validate.
@@ -2152,6 +2117,8 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                parent.addFile(file);
                // set file owner to folder owner
                file.setOwner(parent.getOwner());
+               //set file's readForAll value according to parent folder readForAll value
+               file.setReadForAll(parent.isReadForAll());
 
                final Date now = new Date();
                final AuditInfo auditInfo = new AuditInfo();
@@ -2175,20 +2142,19 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
                // Create the file body.
                try {
-                       createFileBody(name, contentType, fileObject, file, auditInfo, owner);
+                       createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
                } catch (FileNotFoundException e) {
                        throw new GSSIOException(e);
                }
+               touchParentFolders(parent, owner, new Date());
                dao.flush();
                indexFile(file.getId(), false);
 
-               return file.getDTO();
+               return file;
        }
 
-       /* (non-Javadoc)
-        * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFileContents(java.lang.Long, java.lang.Long, java.lang.String, java.io.InputStream)
-        */
-       public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, File fileObject) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
+       @Override
+       public FileHeader updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
                if (fileId == null)
@@ -2213,13 +2179,15 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                auditInfo.setModifiedBy(owner);
                auditInfo.setModificationDate(now);
                try {
-                       createFileBody(file.getName(), contentType, fileObject, file, auditInfo, owner);
+                       createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
                } catch (FileNotFoundException e) {
                        throw new GSSIOException(e);
                }
+               Folder parent = file.getFolder();
+               touchParentFolders(parent, owner, new Date());
 
                indexFile(fileId, false);
-               return file.getDTO();
+               return file;
        }
 
        /**
@@ -2230,7 +2198,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         */
        private String identifyMimeType(String filename) {
                if (filename.indexOf('.') != -1) {
-                       String extension = filename.substring(filename.lastIndexOf('.'));
+                       String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
                        if (".doc".equals(extension))
                                return "application/msword";
                        else if (".xls".equals(extension))
@@ -2239,6 +2207,16 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                                return "application/vnd.ms-powerpoint";
                        else if (".pdf".equals(extension))
                                return "application/pdf";
+                       else if (".gif".equals(extension))
+                               return "image/gif";
+                       else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
+                               return "image/jpeg";
+                       else if (".tiff".equals(extension) || ".tif".equals(extension))
+                               return "image/tiff";
+                       else if (".png".equals(extension))
+                               return "image/png";
+                       else if (".bmp".equals(extension))
+                               return "image/bmp";
                }
                // when all else fails assign the default mime type
                return DEFAULT_MIME_TYPE;
@@ -2250,16 +2228,28 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
         *
         * @param name the original file name
         * @param mimeType the content type
-        * @param uploadedFile the uploaded file contents
+        * @param fileSize the uploaded file size
+        * @param filePath the uploaded file full path
         * @param header the file header that will be associated with the new body
         * @param auditInfo the audit info
-        * @param owner the owner of the file
         * @throws FileNotFoundException
         * @throws QuotaExceededException
+        * @throws ObjectNotFoundException if the owner was not found
         */
-       private void createFileBody(String name, String mimeType, File uploadedFile,
-                               FileHeader header, AuditInfo auditInfo, User owner)
-                       throws FileNotFoundException, QuotaExceededException {
+       private void createFileBody(String name, String mimeType, long fileSize, String filePath,
+                               FileHeader header, AuditInfo auditInfo)
+                       throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
+
+               long currentTotalSize = 0;
+               if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
+                       currentTotalSize = header.getTotalSize();
+               Long quotaLeft = getQuotaLeft(header.getOwner().getId());
+               if(quotaLeft < fileSize-currentTotalSize) {
+                       // quota exceeded -> delete the file
+                       deleteActualFile(filePath);
+                       throw new QuotaExceededException("Not enough free space available");
+               }
+
                FileBody body = new FileBody();
 
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
@@ -2268,11 +2258,11 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
                        body.setMimeType(identifyMimeType(name));
                else
-               body.setMimeType(mimeType);
+                       body.setMimeType(mimeType);
                body.setAuditInfo(auditInfo);
-               body.setFileSize(uploadedFile.length());
+               body.setFileSize(fileSize);
                body.setOriginalFilename(name);
-               body.setStoredFilePath(uploadedFile.getAbsolutePath());
+               body.setStoredFilePath(filePath);
                //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
                if(!header.isVersioned() && header.getCurrentBody() != null){
                        header.setCurrentBody(null);
@@ -2280,25 +2270,22 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                                Iterator<FileBody> it = header.getBodies().iterator();
                                while(it.hasNext()){
                                        FileBody bo = it.next();
-                                       File fileContents = new File(bo.getStoredFilePath());
-                                       if (!fileContents.delete())
-                                               logger.error("Could not delete file " + bo.getStoredFilePath());
+                                       deleteActualFile(bo.getStoredFilePath());
                                        it.remove();
                                        dao.delete(bo);
                                }
                        }
                }
 
-               Long quotaLeft = getQuotaLeft(owner.getId());
-               if(quotaLeft < uploadedFile.length())
-                       throw new QuotaExceededException("Not enough free space available");
                dao.flush();
                header.addBody(body);
+               header.setAuditInfo(auditInfo);
 
                dao.create(body);
        }
 
 
+       @Override
        @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
        public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
                if (userId == null)
@@ -2332,6 +2319,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
 
+       @Override
        public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
 
                if (userId == null)
@@ -2354,6 +2342,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
 
        }
 
+       @Override
        public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -2368,45 +2357,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
        }
 
        @Override
-       public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               final User user = dao.getEntityById(User.class, userId);
-               final Folder folder = dao.getEntityById(Folder.class, folderId);
-               // Check permissions
-               if (!folder.hasReadPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
-               List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
-               if (folder.hasReadPermission(user))
-                       for (Folder f : folder.getSubfolders())
-                               if (f.hasReadPermission(user) && !f.isDeleted())
-                                       subfolders.add(f.getDTO());
-               FolderDTO result = folder.getDTO();
-               result.setSubfolders(subfolders);
-               return folder.getDTO();
-       }
-
-       @Override
-       public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
-               if (userId == null)
-                       throw new ObjectNotFoundException("No user specified");
-               if (folderId == null)
-                       throw new ObjectNotFoundException("No folder specified");
-               User user = dao.getEntityById(User.class, callingUserId);
-               Folder folder = dao.getEntityById(Folder.class, folderId);
-               // Check permissions
-               if (!folder.hasReadPermission(user))
-                       throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
-
-               FolderDTO result = folder.getDTO();
-               result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
-               return result;
-       }
-
-       @Override
-       public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
+       public FileBody getFileVersion(Long userId, Long fileId, int version)
                        throws ObjectNotFoundException, InsufficientPermissionsException {
                if (userId == null)
                        throw new ObjectNotFoundException("No user specified");
@@ -2419,7 +2370,7 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
                FileBody body = dao.getFileVersion(fileId, version);
-               return body.getDTO();
+               return body;
        }
 
        @Override
@@ -2450,4 +2401,414 @@ public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
                return true;
        }
 
+       @Override
+       public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
+               if (userId == null)
+                       throw new ObjectNotFoundException("No user specified");
+               User user = dao.getEntityById(User.class, userId);
+               user.generateWebDAVPassword();
+               return user.getWebDAVPassword();
+       }
+
+       @Override
+       public Invitation findInvite(String code) {
+               if (code == null)
+                       return null;
+               return dao.findInvite(code);
+       }
+
+       @Override
+       public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
+               LDAPConnection lc = new LDAPConnection();
+        LDAPAttributeSet attributeSet = new LDAPAttributeSet();
+        attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
+        attributeSet.add(new LDAPAttribute("uid", username));
+        attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
+        attributeSet.add(new LDAPAttribute("sn", lastname));
+        attributeSet.add(new LDAPAttribute("givenName", firstname));
+        attributeSet.add(new LDAPAttribute("mail", email));
+        attributeSet.add(new LDAPAttribute("userPassword", password));
+        String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
+        LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
+        try {
+               lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
+               lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
+                               getConfiguration().getString("bindPassword").getBytes("UTF8"));
+               lc.add(newEntry);
+               logger.info("Successfully added LDAP account: " + dn);
+               lc.disconnect();
+        } catch(LDAPException e) {
+               throw new RuntimeException(e);
+        } catch(UnsupportedEncodingException e) {
+               throw new RuntimeException(e);
+        }
+
+       }
+
+       @Override
+       public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
+               User user = findUser(username);
+               if (user == null)
+                       throw new ObjectNotFoundException("The user was not found");
+               Invitation invite = findInvite(code);
+               if (invite.getUser() != null)
+                       throw new InvitationUsedException("This code has already been used");
+               invite.setUser(user);
+               UserClass couponClass = getCouponUserClass();
+               user.setUserClass(couponClass);
+               return couponClass;
+       }
+
+       @Override
+       public UserClass getCouponUserClass() {
+               return dao.findCouponUserClass();
+       }
+
+       /**
+        * Set the provided readForAll as the new readforAll value of the specified
+        * folder and sub-folders.
+        *
+        * @param user
+        * @param folder
+        * @param readForAll
+        * @throws ObjectNotFoundException
+        *
+        */
+       private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
+               if (readForAll != null && user.equals(folder.getOwner())){
+                       folder.setReadForAll(readForAll);
+                       dao.update(folder);
+                       for (FileHeader file : folder.getFiles())
+                               file.setReadForAll(readForAll);
+                       if(readForAll)
+                               //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
+                               for (Folder sub : folder.getSubfolders())
+                                       setFolderReadForAll(user, sub, readForAll);
+
+               }
+
+       }
+               
+       /**
+        * Update the userLogin with the values from the supplied object.
+        */
+       
+       public void addUserLogin(UserLogin userLogin) {
+               dao.update(userLogin);          
+
+       }
+               
+       /**
+        * Retrieves the current session user login and the user's last login
+        * 
+        * @param userId
+        * @return a list of last two user logins
+        * @throws ObjectNotFoundException 
+        */
+       
+       public List<UserLogin> getLastUserLogins(Long userId) throws ObjectNotFoundException{
+               List<UserLogin> userLoginResults = new ArrayList<UserLogin>();          
+               userLoginResults = dao.getLoginsForUser(userId);        
+               if(userLoginResults.size() == 0)
+                       throw new ObjectNotFoundException("No userlogin found for the user");
+               //if the user logins for the first time lastLoginDate = currentLoginDate
+               if(userLoginResults.size()==1)
+                       userLoginResults.add(userLoginResults.get(0));
+               return userLoginResults;
+       }
+       
+
+    public void postFileToSolr(Long id) throws IOException, SolrServerException, ObjectNotFoundException {
+        CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
+        postFileToSolr(solr, id);
+        solr.commit();
+    }
+
+       private void postFileToSolr(CommonsHttpSolrServer solr, Long id) throws ObjectNotFoundException {
+               try {
+                       FileHeader file = dao.getFileForIndexing(id);
+                       FileBody body = file.getCurrentBody();
+                       String mime = body.getMimeType();
+                       boolean multipart = true;
+                       if (!mime.equals("application/pdf") 
+                                               && !mime.equals("text/plain")
+                                               && !mime.equals("text/html")
+                                               && !mime.endsWith("msword")
+                                               && !mime.endsWith("ms-excel")
+                                               && !mime.endsWith("powerpoint")
+                                               || (body.getFileSize() > getConfiguration().getLong("solrDocumentUploadLimitInKB") * 1024))
+                               multipart = false;
+
+                       if (!multipart)
+                               sendMetaDataOnly(solr, file);
+                       else {
+                ContentStreamUpdateRequest solrRequest = new ContentStreamUpdateRequest(getConfiguration().getString("solr.rich.update.path"));
+                               solrRequest.setParam("literal.id", file.getId().toString());
+                               solrRequest.setParam("literal.name", file.getName());
+                               for (FileTag t : file.getFileTags()) {
+                                       solrRequest.getParams().add("literal.tag", t.getTag());
+                               }
+                for (Permission p : file.getPermissions()) {
+                    if (p.getRead()) {
+                        if (p.getUser() != null)
+                            solrRequest.getParams().add("literal.ureaders", p.getUser().getId().toString());
+                        else if (p.getGroup() != null)
+                            solrRequest.getParams().add("literal.greaders", p.getGroup().getId().toString());
+                    }
+                }
+                solrRequest.setParam("literal.owner", file.getOwner().getId().toString());
+                solrRequest.setParam("literal.public", String.valueOf(file.isReadForAll()));
+                File fsFile = new File(body.getStoredFilePath());
+                               solrRequest.addFile(fsFile);
+                               try {
+                                       solr.request(solrRequest);
+                               }
+                               catch (SolrException e) {
+                                       logger.warn("File " + id + " failed with SolrException: " + e.getLocalizedMessage() + ". Retrying without the file");
+                                       //Let 's try without the file
+                                       sendMetaDataOnly(solr, file);
+                               }
+                               catch (NullPointerException e) {
+                                       logger.warn("File " + id + " failed with NullPointerException: " + e.getLocalizedMessage() + ". Retrying without the file");
+                                       //Let 's try without the file
+                                       sendMetaDataOnly(solr, file);
+                               }
+                               catch (SolrServerException e) {
+                                       logger.warn("File " + id + " failed with SolrServerException: " + e.getLocalizedMessage() + ". Retrying without the file");
+                                       //Let 's try without the file
+                                       sendMetaDataOnly(solr, file);
+                               }
+                       }
+               } catch (MalformedURLException e) {
+                       throw new EJBException(e);
+               } catch (SolrServerException e) {
+                       throw new EJBException(e);
+               } catch (IOException e) {
+                       throw new EJBException(e);
+               }
+       }
+
+       private void sendMetaDataOnly(CommonsHttpSolrServer solr, FileHeader file) throws SolrServerException, IOException {
+               SolrInputDocument solrDoc = new SolrInputDocument();
+               solrDoc.addField("id", file.getId().toString());
+               solrDoc.addField("name", file.getName());
+               for (FileTag t : file.getFileTags()) {
+                       solrDoc.addField("tag", t.getTag());
+               }
+        for (Permission p : file.getPermissions()) {
+            if (p.getRead()) {
+                if (p.getUser() != null)
+                    solrDoc.addField("ureaders", p.getUser().getId());
+                else if (p.getGroup() != null)
+                    solrDoc.addField("greaders", p.getGroup().getId());
+            }
+        }
+        solrDoc.addField("owner", file.getOwner().getId());
+        solrDoc.addField("public", file.isReadForAll());
+               solr.add(solrDoc);
+       }
+
+       private String tokenizeFilename(String filename){
+               StringBuffer result = new StringBuffer();
+               StringTokenizer tokenizer = new StringTokenizer(filename,"._");
+               while(tokenizer.hasMoreTokens()){
+                       result.append(tokenizer.nextToken());
+                       result.append(" ");
+               }
+               result.append(filename);
+               return result.toString();
+       }
+
+       private String normalizeSearchQuery(String query) {
+               if (query.contains("*"))
+                       return query.toLowerCase().replace('ά', 'α').replace('έ', 'ε').replace('ί', 'ι').replace('ή', 'η').replace('ύ', 'υ')
+                                       .replace('ό', 'ο').replace('ς', 'σ').replace('ώ', 'ω').replace('ϊ', 'ι').replace('ϋ', 'υ');
+               else
+                       return query;
+       }
+       
+       private String escapeCharacters(String text) {
+               return text.replaceAll(":", "\\\\:");
+       }
+       
+       /*** NEW METHODS IN ORDER TO AVOID LAZY loading exception in json render 
+        ****/
+       @Override
+       public Folder expandFolder(Folder folder) throws ObjectNotFoundException{
+               Folder result = dao.getEntityById(Folder.class, folder.getId());
+               result.getSubfolders().size();
+               result.getFiles().size();
+               result.getPermissions().size();
+               return result;
+}
+
+       @Override
+       public FileHeader expandFile(FileHeader folder) throws ObjectNotFoundException{
+               FileHeader result = dao.getEntityById(FileHeader.class, folder.getId());
+               result.getFolder();
+               result.getPermissions().size();
+               result.getFileTags().size();
+               return result;
+       }
+       
+       @Override
+       public Group expandGroup(Group folder) throws ObjectNotFoundException{
+               Group result = dao.getEntityById(Group.class, folder.getId());
+               result.getMembers().size();
+               return result;
+       }
+
+       /* (non-Javadoc)
+        * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
+        */
+       @Override
+       public User getUserByUserName(String username) {
+               User result = dao.getUserByUserName(username);
+               return result;
+       }
+       
+       /*WEBDAV CREATE EMPTY FILE*/
+       @Override
+       public FileHeader createEmptyFile(Long userId, Long folderId, String name)
+                       throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
+                       InsufficientPermissionsException, QuotaExceededException {
+               // Validate.
+               if (userId == null)
+                       throw new ObjectNotFoundException("No user specified");
+               if (folderId == null)
+                       throw new ObjectNotFoundException("No folder specified");
+               String contentType = DEFAULT_MIME_TYPE;
+               if (StringUtils.isEmpty(name))
+                       throw new ObjectNotFoundException("No file name specified");
+               if (dao.existsFolderOrFile(folderId, name))
+                       throw new DuplicateNameException("A folder or file with the name '" + name +
+                                               "' already exists at this level");
+
+               // Do the actual work.
+               Folder parent = null;
+               try {
+                       parent = dao.getEntityById(Folder.class, folderId);
+               } catch (final ObjectNotFoundException onfe) {
+                       // Supply a more accurate problem description.
+                       throw new ObjectNotFoundException("Parent folder not found");
+               }
+               final User owner = dao.getEntityById(User.class, userId);
+               if (!parent.hasWritePermission(owner))
+                       throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
+               final FileHeader file = new FileHeader();
+               file.setName(name);
+               parent.addFile(file);
+               // set file owner to folder owner
+               file.setOwner(parent.getOwner());
+               //set file's readForAll value according to parent folder readForAll value
+               file.setReadForAll(parent.isReadForAll());
+
+               final Date now = new Date();
+               final AuditInfo auditInfo = new AuditInfo();
+               auditInfo.setCreatedBy(owner);
+               auditInfo.setCreationDate(now);
+               auditInfo.setModifiedBy(owner);
+               auditInfo.setModificationDate(now);
+               file.setAuditInfo(auditInfo);
+               // TODO set the proper versioning flag on creation
+               file.setVersioned(false);
+
+               for (final Permission p : parent.getPermissions()) {
+                       final Permission permission = new Permission();
+                       permission.setGroup(p.getGroup());
+                       permission.setUser(p.getUser());
+                       permission.setRead(p.getRead());
+                       permission.setWrite(p.getWrite());
+                       permission.setModifyACL(p.getModifyACL());
+                       file.addPermission(permission);
+               }
+               // Create the file body.
+               try {
+                       createEmptyFileBody(name, contentType, 0,  file, auditInfo);
+               } catch (FileNotFoundException e) {
+                       throw new GSSIOException(e);
+               }
+               touchParentFolders(parent, owner, new Date());
+               dao.flush();
+               return file;
+       }
+       
+       private void createEmptyFileBody(String name, String mimeType, long fileSize, 
+                               FileHeader header, AuditInfo auditInfo)
+                       throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
+
+               long currentTotalSize = 0;
+               if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
+                       currentTotalSize = header.getTotalSize();
+               Long quotaLeft = getQuotaLeft(header.getOwner().getId());
+               
+
+               FileBody body = new FileBody();
+
+               // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
+               if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
+                                       || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
+                                       || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
+                       body.setMimeType(identifyMimeType(name));
+               else
+                       body.setMimeType(mimeType);
+               body.setAuditInfo(auditInfo);
+               body.setFileSize(fileSize);
+               body.setOriginalFilename(name);
+               body.setStoredFilePath(generateRepositoryFilePath());
+               //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
+               if(!header.isVersioned() && header.getCurrentBody() != null){
+                       header.setCurrentBody(null);
+                       if (header.getBodies() != null) {
+                               Iterator<FileBody> it = header.getBodies().iterator();
+                               while(it.hasNext()){
+                                       FileBody bo = it.next();
+                                       deleteActualFile(bo.getStoredFilePath());
+                                       it.remove();
+                                       dao.delete(bo);
+                               }
+                       }
+               }
+
+               dao.flush();
+               header.addBody(body);
+               header.setAuditInfo(auditInfo);
+
+               dao.create(body);
+       }
+       /*** WEBDAV LOCK **/
+       @Override
+       public FileLock getLockById(String id) {
+               return dao.getLockById(id);
+       }
+
+       @Override
+       public FileLock getLockByToken(String tokenId) {
+               return dao.getLockByToken(tokenId);
+       }
+
+       @Override
+       public void removeLock(FileLock lock) {
+               dao.removeLock(lock);           
+       }
+
+       @Override
+       public FileLock saveOrUpdateLock(FileLock lock) {
+               return dao.saveOrUpdateLock(lock);
+       }
+       
+       @Override
+       public WebDavNonce getWebDavNonce(String tokenId) {
+               return dao.getWebDavNonce(tokenId);
+       }
+
+       @Override
+       public void removeWebDavNonce(WebDavNonce nonce) {
+               dao.removeWebDavNonce(nonce);           
+       }
+
+       @Override
+       public WebDavNonce saveOrUpdateWebDavNonce(WebDavNonce nonce) {
+               return dao.saveOrUpdateWebDavNonce(nonce);
+       }
 }