Allow setting the modification date when updating a file.
[pithos] / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java
1 /*
2  * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server.ejb;
20
21 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22 import gr.ebs.gss.client.exceptions.DuplicateNameException;
23 import gr.ebs.gss.client.exceptions.GSSIOException;
24 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
25 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
26 import gr.ebs.gss.client.exceptions.QuotaExceededException;
27 import gr.ebs.gss.server.domain.AuditInfo;
28 import gr.ebs.gss.server.domain.FileBody;
29 import gr.ebs.gss.server.domain.FileHeader;
30 import gr.ebs.gss.server.domain.FileTag;
31 import gr.ebs.gss.server.domain.FileUploadStatus;
32 import gr.ebs.gss.server.domain.Folder;
33 import gr.ebs.gss.server.domain.Group;
34 import gr.ebs.gss.server.domain.Nonce;
35 import gr.ebs.gss.server.domain.Permission;
36 import gr.ebs.gss.server.domain.User;
37 import gr.ebs.gss.server.domain.dto.FileBodyDTO;
38 import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
39 import gr.ebs.gss.server.domain.dto.FolderDTO;
40 import gr.ebs.gss.server.domain.dto.GroupDTO;
41 import gr.ebs.gss.server.domain.dto.PermissionDTO;
42 import gr.ebs.gss.server.domain.dto.StatsDTO;
43 import gr.ebs.gss.server.domain.dto.UserDTO;
44
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.FileNotFoundException;
48 import java.io.FileOutputStream;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.StringWriter;
52 import java.io.UnsupportedEncodingException;
53 import java.util.ArrayList;
54 import java.util.Date;
55 import java.util.Iterator;
56 import java.util.LinkedHashSet;
57 import java.util.LinkedList;
58 import java.util.List;
59 import java.util.Locale;
60 import java.util.Random;
61 import java.util.Set;
62 import java.util.StringTokenizer;
63
64 import javax.ejb.EJB;
65 import javax.ejb.EJBException;
66 import javax.ejb.Stateless;
67 import javax.ejb.TransactionAttribute;
68 import javax.ejb.TransactionAttributeType;
69 import javax.jms.Connection;
70 import javax.jms.ConnectionFactory;
71 import javax.jms.JMSException;
72 import javax.jms.MapMessage;
73 import javax.jms.MessageProducer;
74 import javax.jms.Queue;
75 import javax.jms.QueueConnectionFactory;
76 import javax.jms.Session;
77 import javax.jws.WebMethod;
78 import javax.naming.Context;
79 import javax.naming.InitialContext;
80 import javax.naming.NamingException;
81 import javax.xml.parsers.DocumentBuilder;
82 import javax.xml.parsers.DocumentBuilderFactory;
83 import javax.xml.parsers.ParserConfigurationException;
84 import javax.xml.transform.OutputKeys;
85 import javax.xml.transform.Transformer;
86 import javax.xml.transform.TransformerConfigurationException;
87 import javax.xml.transform.TransformerException;
88 import javax.xml.transform.TransformerFactory;
89 import javax.xml.transform.dom.DOMSource;
90 import javax.xml.transform.stream.StreamResult;
91
92 import org.apache.commons.httpclient.HttpClient;
93 import org.apache.commons.httpclient.HttpException;
94 import org.apache.commons.httpclient.NameValuePair;
95 import org.apache.commons.httpclient.methods.GetMethod;
96 import org.apache.commons.httpclient.methods.PostMethod;
97 import org.apache.commons.httpclient.methods.StringRequestEntity;
98 import org.apache.commons.lang.StringUtils;
99 import org.apache.commons.logging.Log;
100 import org.apache.commons.logging.LogFactory;
101 import org.w3c.dom.DOMException;
102 import org.w3c.dom.Document;
103 import org.w3c.dom.Node;
104 import org.w3c.dom.NodeList;
105 import org.xml.sax.SAXException;
106
107 /**
108  * The concrete implementation of the ExternalAPI interface.
109  *
110  * @author past
111  */
112 @Stateless
113 public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
114         /**
115          * The default MIME type for files without an explicit one.
116          */
117         private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
118
119         /**
120          * The size of the buffer that is used to temporarily store chunks of
121          * uploaded files, while storing them to the file repository.
122          */
123         private static final int UPLOAD_BUFFER_SIZE = 1024 * 4;
124
125         /**
126          * The logger.
127          */
128         private static Log logger = LogFactory.getLog(ExternalAPIBean.class);
129
130         /**
131          * Injected reference to the GSSDAO data access facade.
132          */
133         @EJB
134         private GSSDAO dao;
135
136
137         /**
138          * A cached random number generator for creating unique filenames.
139          */
140         private static Random random = new Random();
141
142         private void touchParentFolders(Folder folder, User modifiedBy, Date modificationDate) {
143                 Folder f = folder;
144                 while (f!=null) {
145                         AuditInfo ai = f.getAuditInfo();
146                         ai.setModifiedBy(modifiedBy);
147                         ai.setModificationDate(modificationDate);
148                         f.setAuditInfo(ai);
149                         f = f.getParent();
150                 }
151         }
152
153         @Override
154         public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException {
155                 if (userId == null)
156                         throw new ObjectNotFoundException("No user specified");
157                 Folder folder = dao.getRootFolder(userId);
158                 return folder.getDTO();
159         }
160
161         /*
162          * (non-Javadoc)
163          *
164          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolder(java.lang.Long)
165          */
166         public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
167                 if (userId == null)
168                         throw new ObjectNotFoundException("No user specified");
169                 if (folderId == null)
170                         throw new ObjectNotFoundException("No folder specified");
171                 final User user = dao.getEntityById(User.class, userId);
172                 final Folder folder = dao.getEntityById(Folder.class, folderId);
173                 // Check permissions
174                 if (!folder.hasReadPermission(user))
175                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
176                 return folder.getDTO();
177         }
178
179         /* (non-Javadoc)
180          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUser(java.lang.Long)
181          */
182         public User getUser(Long userId) throws ObjectNotFoundException {
183                 if (userId == null)
184                         throw new ObjectNotFoundException("No user specified");
185                 return dao.getEntityById(User.class, userId);
186         }
187
188         /* (non-Javadoc)
189          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserDTO(java.lang.Long)
190          */
191         public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException {
192                 return getUser(userId).getDTO();
193         }
194
195         /*
196          * (non-Javadoc)
197          *
198          * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroup(java.lang.Long)
199          */
200         public GroupDTO getGroup(final Long groupId) throws ObjectNotFoundException {
201                 if (groupId == null)
202                         throw new ObjectNotFoundException("No group specified");
203                 final Group group = dao.getEntityById(Group.class, groupId);
204                 return group.getDTO();
205         }
206
207         @Override
208         public GroupDTO getGroup(Long userId, String name) throws ObjectNotFoundException {
209                 if (userId == null)
210                         throw new ObjectNotFoundException("No user specified");
211                 if (name == null)
212                         throw new ObjectNotFoundException("No group specified");
213                 User user = dao.getEntityById(User.class, userId);
214                 List<Group> groups = user.getGroupsSpecified();
215                 for (Group group: groups)
216                         if (group.getName().equals(name))
217                                 return group.getDTO();
218                 throw new ObjectNotFoundException("Group " + name + " not found");
219         }
220
221         /*
222          * (non-Javadoc)
223          *
224          * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroups(java.lang.Long)
225          */
226         public List<GroupDTO> getGroups(final Long userId) throws ObjectNotFoundException {
227                 if (userId == null)
228                         throw new ObjectNotFoundException("No user specified");
229                 final List<Group> groups = dao.getGroups(userId);
230                 final List<GroupDTO> result = new ArrayList<GroupDTO>();
231                 for (final Group g : groups)
232                         result.add(g.getDTO());
233                 return result;
234         }
235
236         @Override
237         public List<FileHeaderDTO> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
238                         throws ObjectNotFoundException, InsufficientPermissionsException {
239                 // Validate.
240                 if (userId == null)
241                         throw new ObjectNotFoundException("No user specified");
242                 if (folderId == null)
243                         throw new ObjectNotFoundException("No folder specified");
244                 User user = dao.getEntityById(User.class, userId);
245                 Folder folder = dao.getEntityById(Folder.class, folderId);
246                 if (!folder.hasReadPermission(user))
247                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
248                 // Do the actual work.
249                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
250                 List<FileHeader> files = dao.getFiles(folderId, userId, ignoreDeleted);
251                 for (FileHeader f : files)
252                         result.add(f.getDTO());
253                 return result;
254         }
255
256         /*
257          * (non-Javadoc)
258          *
259          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsers(java.lang.Long,
260          *      java.lang.Long)
261          */
262         public List<UserDTO> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
263                 // Validate.
264                 if (userId == null)
265                         throw new ObjectNotFoundException("No user specified");
266                 if (groupId == null)
267                         throw new ObjectNotFoundException("No group specified");
268
269                 // Do the actual work.
270                 final List<User> users = dao.getUsers(groupId);
271                 final List<UserDTO> result = new ArrayList<UserDTO>();
272                 for (final User u : users)
273                         result.add(u.getDTO());
274                 return result;
275         }
276
277         @Override
278         public FolderDTO createFolder(Long userId, Long parentId, String name)
279                         throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
280                 // Validate.
281                 if (userId == null)
282                         throw new ObjectNotFoundException("No user specified");
283                 if (StringUtils.isEmpty(name))
284                         throw new ObjectNotFoundException("New folder name is empty");
285                 if (parentId == null)
286                         throw new ObjectNotFoundException("No parent specified");
287                 if (dao.existsFolderOrFile(parentId, name))
288                         throw new DuplicateNameException("A folder or file with the name '" +
289                                                 name + "' already exists at this level");
290
291                 User creator = dao.getEntityById(User.class, userId);
292
293                 Folder parent = null;
294                 try {
295                         parent = dao.getEntityById(Folder.class, parentId);
296                 } catch (ObjectNotFoundException onfe) {
297                         // Supply a more accurate problem description.
298                         throw new ObjectNotFoundException("Parent folder not found");
299                 }
300                 if (!parent.hasWritePermission(creator))
301                         throw new InsufficientPermissionsException("You don't have the permissions" +
302                                         " to write to this folder");
303
304                 // Do the actual work.
305                 return createFolder(name, parent, creator);
306         }
307
308         /**
309          * Create a new folder with the provided name, parent and owner.
310          *
311          * @param name
312          * @param parent
313          * @param creator
314          * @return the new folder
315          */
316         private FolderDTO createFolder(String name, Folder parent, User creator) {
317                 Folder folder = new Folder();
318                 folder.setName(name);
319                 if (parent != null) {
320                         parent.addSubfolder(folder);
321                         folder.setOwner(parent.getOwner());
322                 } else
323                         folder.setOwner(creator);
324
325                 Date now = new Date();
326                 AuditInfo auditInfo = new AuditInfo();
327                 auditInfo.setCreatedBy(creator);
328                 auditInfo.setCreationDate(now);
329                 auditInfo.setModifiedBy(creator);
330                 auditInfo.setModificationDate(now);
331                 folder.setAuditInfo(auditInfo);
332                 touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
333
334                 if (parent != null)
335                         for (Permission p : parent.getPermissions()) {
336                                 Permission permission = new Permission();
337                                 permission.setGroup(p.getGroup());
338                                 permission.setUser(p.getUser());
339                                 permission.setRead(p.getRead());
340                                 permission.setWrite(p.getWrite());
341                                 permission.setModifyACL(p.getModifyACL());
342                                 folder.addPermission(permission);
343                         }
344                 else {
345                         Permission permission = new Permission();
346                         permission.setUser(creator);
347                         permission.setRead(true);
348                         permission.setWrite(true);
349                         permission.setModifyACL(true);
350                         folder.addPermission(permission);
351                 }
352                 dao.create(folder);
353                 return folder.getDTO();
354         }
355
356         /*
357          * Deletes the given folder and all its subfolders and files
358          * Only the permissions for top folder are checked
359          *
360          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFolder(java.lang.Long,
361          *      java.lang.Long)
362          */
363         public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException {
364                 // Validate.
365                 if (userId == null)
366                         throw new ObjectNotFoundException("No user specified");
367                 if (folderId == null)
368                         throw new ObjectNotFoundException("No folder specified");
369
370                 // Do the actual work.
371                 final Folder folder = dao.getEntityById(Folder.class, folderId);
372                 final Folder parent = folder.getParent();
373                 if (parent == null)
374                         throw new ObjectNotFoundException("Deleting the root folder is not allowed");
375                 final User user = dao.getEntityById(User.class, userId);
376                 if (!folder.hasDeletePermission(user)) {
377                         logger.info("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
378                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
379                 }
380                 removeSubfolderFiles(folder);
381                 parent.removeSubfolder(folder);
382                 dao.delete(folder);
383                 touchParentFolders(parent, user, new Date());
384         }
385
386         /**
387          * Traverses the folder and deletes all actual files (file system)
388          * regardless of permissions
389          *
390          * @param folder
391          */
392         private void removeSubfolderFiles(Folder folder) {
393                 //remove files for all subfolders
394                 for (Folder subfolder:folder.getSubfolders())
395                         removeSubfolderFiles(subfolder);
396                 //remove this folder's file bodies (actual files)
397                 for (FileHeader file:folder.getFiles()) {
398                         for (FileBody body:file.getBodies())
399                                 deleteActualFile(body.getStoredFilePath());
400                         indexFile(file.getId(), true);
401                 }
402         }
403
404         @SuppressWarnings("unchecked")
405         public List<FolderDTO> getSubfolders(Long userId, Long folderId)
406                         throws ObjectNotFoundException, InsufficientPermissionsException {
407                 if (userId == null)
408                         throw new ObjectNotFoundException("No user specified");
409                 if (folderId == null)
410                         throw new ObjectNotFoundException("No folder specified");
411                 User user = dao.getEntityById(User.class, userId);
412                 Folder folder = dao.getEntityById(Folder.class, folderId);
413                 if (!folder.hasReadPermission(user))
414                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
415                 List<FolderDTO> result = new ArrayList<FolderDTO>();
416                 if (folder.hasReadPermission(user))
417                         for (Folder f : folder.getSubfolders())
418                                 if (f.hasReadPermission(user) && !f.isDeleted())
419                                         result.add(f.getDTO());
420                 return result;
421         }
422
423         @Override
424         public FolderDTO modifyFolder(Long userId, Long folderId, String folderName)
425                         throws InsufficientPermissionsException, ObjectNotFoundException, DuplicateNameException {
426
427                 // Validate.
428                 if (userId == null)
429                         throw new ObjectNotFoundException("No user specified");
430                 if (folderId == null)
431                         throw new ObjectNotFoundException("No folder specified");
432                 if (StringUtils.isEmpty(folderName))
433                         throw new ObjectNotFoundException("New folder name is empty");
434
435                 Folder folder = dao.getEntityById(Folder.class, folderId);
436                 User user = dao.getEntityById(User.class, userId);
437                 if (!folder.hasWritePermission(user))
438                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
439
440                 Folder parent = folder.getParent();
441                 if (parent != null)
442                         if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
443                                 throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
444
445                 // Do the actual modification.
446                 folder.setName(folderName);
447                 dao.update(folder);
448                 touchParentFolders(folder, user, new Date());
449                 return folder.getDTO();
450         }
451
452         /*
453          * (non-Javadoc)
454          *
455          * @see gr.ebs.gss.server.ejb.ExternalAPI#createGroup(java.lang.Long,
456          *      java.lang.String)
457          */
458         public void createGroup(final Long userId, final String name) throws ObjectNotFoundException, DuplicateNameException {
459                 // Validate.
460                 if (userId == null)
461                         throw new ObjectNotFoundException("No user specified");
462                 if (StringUtils.isEmpty(name))
463                         throw new ObjectNotFoundException("New group name is empty");
464                 if (name.indexOf('/')>=0)
465                         throw new IllegalArgumentException("Character '/' is not allowed in group name");
466                 if (dao.existsGroup(userId, name))
467                         throw new DuplicateNameException("A group with the name '" + name + "' already exists");
468
469                 // TODO: Check permissions
470
471                 final User owner = dao.getEntityById(User.class, userId);
472
473                 // Do the actual work.
474                 owner.createGroup(name);
475         }
476
477         /*
478          * (non-Javadoc)
479          *
480          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteGroup(java.lang.Long,
481          *      java.lang.Long)
482          */
483         public void deleteGroup(final Long userId, final Long groupId) throws ObjectNotFoundException, InsufficientPermissionsException {
484                 // Validate.
485                 if (userId == null)
486                         throw new ObjectNotFoundException("No user specified");
487                 if (groupId == null)
488                         throw new ObjectNotFoundException("No group specified");
489
490                 // Do the actual work.
491                 final User owner = dao.getEntityById(User.class, userId);
492                 final Group group = dao.getEntityById(Group.class, groupId);
493                 // Only delete the group if actually owned by the user.
494                 if (group.getOwner().equals(owner)) {
495                         List<Folder> folders = dao.getFoldersPermittedForGroup(userId, groupId);
496                         for (Folder f : folders){
497                                 f.getPermissions().removeAll(group.getPermissions());
498                                 for(FileHeader file : f.getFiles())
499                                         file.getPermissions().removeAll(group.getPermissions());
500                         }
501                         List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
502                         for(FileHeader h : files)
503                                 h.getPermissions().removeAll(group.getPermissions());
504                         owner.removeSpecifiedGroup(group);
505                         dao.delete(group);
506                 }
507                 else throw new InsufficientPermissionsException("You are not the owner of this group");
508         }
509
510         @Override
511         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, InputStream stream)
512                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
513                         InsufficientPermissionsException, QuotaExceededException {
514                 File file = null;
515                 try {
516                         file = uploadFile(stream, userId);
517                 } catch ( IOException ioe) {
518                         // Supply a more accurate problem description.
519                         throw new GSSIOException("Problem creating file",ioe);
520                 }
521                 return createFile(userId, folderId, name, mimeType, file.length(), file.getAbsolutePath());
522         }
523
524         /* (non-Javadoc)
525          * @see gr.ebs.gss.server.ejb.ExternalAPIRemote#indexFile(java.lang.Long, boolean)
526          */
527         public void indexFile(Long fileId, boolean delete) {
528                 Connection qConn = null;
529                 Session session = null;
530                 MessageProducer sender = null;
531                 try {
532                         Context jndiCtx = new InitialContext();
533                         ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
534                         Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
535                         qConn = factory.createConnection();
536                         session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
537                         sender = session.createProducer(queue);
538
539                         MapMessage map = session.createMapMessage();
540                         map.setObject("id", fileId);
541                         map.setBoolean("delete", delete);
542                         sender.send(map);
543                 }
544                 catch (NamingException e) {
545                         logger.error("Index was not updated: ", e);
546                 }
547                 catch (JMSException e) {
548                         logger.error("Index was not updated: ", e);
549                 }
550                 finally {
551                         try {
552                                 if (sender != null)
553                                         sender.close();
554                                 if (session != null)
555                                         session.close();
556                                 if (qConn != null)
557                                         qConn.close();
558                         }
559                         catch (JMSException e) {
560                                 logger.warn(e);
561                         }
562                 }
563         }
564
565
566
567         /**
568          * A helper method that generates a unique file path for a stored file. The
569          * files are stored using random hash names that are distributed evenly in
570          * a 2-level tree of subdirectories named after the first two hex characters
571          * in the name. For example, file ab1234cd5769f will be stored in the path
572          * /file-repository-root/a/b/ab1234cd5769f. The directories will be created
573          * if they don't already exist.
574          *
575          * @return a unique new file path
576          */
577         private String generateRepositoryFilePath() {
578                 String filename = Long.toHexString(random.nextLong());
579                 String fileRepositoryPath = getConfiguration().getString("fileRepositoryPath","/tmp");
580                 File root = new File(fileRepositoryPath);
581                 if (!root.exists())
582                         root.mkdirs();
583                 File firstFolder = new File(root + File.separator + filename.substring(0, 1));
584                 if (!firstFolder.exists())
585                         firstFolder.mkdir();
586                 File secondFolder = new File(firstFolder + File.separator + filename.substring(1, 2));
587                 if (!secondFolder.exists())
588                         secondFolder.mkdir();
589                 return secondFolder + File.separator + filename;
590         }
591
592         /*
593          * (non-Javadoc)
594          *
595          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFile(java.lang.Long,
596          *      java.lang.Long)
597          */
598         public void deleteFile(final Long userId, final Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
599                 // Validate.
600                 if (userId == null)
601                         throw new ObjectNotFoundException("No user specified");
602                 if (fileId == null)
603                         throw new ObjectNotFoundException("No file specified");
604
605                 // Do the actual work.
606                 final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
607                 final Folder parent = file.getFolder();
608                 if (parent == null)
609                         throw new ObjectNotFoundException("The specified file has no parent folder");
610                 final User user = dao.getEntityById(User.class, userId);
611                 if (!file.hasDeletePermission(user))
612                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
613                 for (final FileBody body : file.getBodies())
614                         deleteActualFile(body.getStoredFilePath());
615                 dao.delete(file);
616                 touchParentFolders(parent, user, new Date());
617                 indexFile(fileId, true);
618         }
619
620         private void deleteActualFile(String filePath) {
621                 if (filePath == null)
622                         return;
623                 File file = new File(filePath);
624                 if (!file.delete())
625                         logger.error("Could not delete file " + filePath);
626         }
627
628         /*
629          * (non-Javadoc)
630          *
631          * @see gr.ebs.gss.server.ejb.ExternalAPI#createTag(java.lang.Long,
632          *      java.lang.Long, java.lang.String)
633          */
634         public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
635                 if (userId == null)
636                         throw new ObjectNotFoundException("No user specified");
637                 if (fileHeaderId == null)
638                         throw new ObjectNotFoundException("No file specified");
639                 if (StringUtils.isEmpty(tag))
640                         throw new ObjectNotFoundException("Tag is empty");
641
642                 final User user = dao.getEntityById(User.class, userId);
643                 final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId);
644                 final Folder parent = fh.getFolder();
645                 if (parent == null)
646                         throw new ObjectNotFoundException("The specified file has no parent folder");
647                 user.addTag(fh, tag);
648                 touchParentFolders(parent, user, new Date());
649         }
650
651         /* (non-Javadoc)
652          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserTags(java.lang.Long)
653          */
654         @WebMethod(operationName = "getUserTags")
655         public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
656                 return dao.getUserTags(userId);
657         }
658
659         public void updateFile(Long userId, Long fileId, String name,
660                                 String tagSet, Date modificationDate)
661                         throws ObjectNotFoundException, InsufficientPermissionsException {
662                 if (userId == null)
663                         throw new ObjectNotFoundException("No user specified");
664                 if (fileId == null)
665                         throw new ObjectNotFoundException("No file specified");
666                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
667                 final Folder parent = file.getFolder();
668                 if (parent == null)
669                         throw new ObjectNotFoundException("The specified file has no parent folder");
670
671                 User user = dao.getEntityById(User.class, userId);
672                 if (!file.hasWritePermission(user))
673                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot update file " + file.getName() + "(" + file.getId() + ")");
674
675                 if (name != null)
676                         file.setName(name);
677                 if (modificationDate != null) {
678                         file.getAuditInfo().setModificationDate(modificationDate);
679                         file.getAuditInfo().setModifiedBy(user);
680                 }
681                 List<FileTag> tags = file.getFileTags();
682
683                 if (tagSet != null) {
684                         Iterator<FileTag> i = tags.iterator();
685                         while (i.hasNext()) {
686                                 FileTag tag = i.next();
687                                 i.remove();
688                                 tag.setFile(null);
689                                 user.removeTag(tag);
690                                 dao.delete(tag);
691                         }
692                         dao.flush();
693                         StringTokenizer st = new StringTokenizer(tagSet, ",");
694                         while (st.hasMoreTokens())
695                                 new FileTag(user, file, st.nextToken().trim());
696                 }
697                 touchParentFolders(parent, user, new Date());
698
699                 // Re-index the file if it was modified.
700                 if (name != null || tagSet != null)
701                         indexFile(fileId, false);
702         }
703
704         @Override
705         public InputStream getFileContents(Long userId, Long fileId)
706                         throws ObjectNotFoundException, InsufficientPermissionsException {
707                 if (userId == null)
708                         throw new ObjectNotFoundException("No user specified");
709                 if (fileId == null)
710                         throw new ObjectNotFoundException("No file specified");
711
712                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
713                 User user = dao.getEntityById(User.class, userId);
714                 if (!header.hasReadPermission(user)) {
715                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
716                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
717                 }
718
719                 File f = new File(header.getCurrentBody().getStoredFilePath());
720                 try {
721                         return new FileInputStream(f);
722                 } catch (FileNotFoundException e) {
723                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
724                         throw new ObjectNotFoundException("The file contents could not be located");
725                 }
726         }
727
728         /* (non-Javadoc)
729          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long)
730          */
731         public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
732                 if (userId == null)
733                         throw new ObjectNotFoundException("No user specified");
734                 if (fileId == null)
735                         throw new ObjectNotFoundException("No file specified");
736                 if (bodyId == null)
737                         throw new ObjectNotFoundException("No file specified");
738
739                 final FileHeader header = dao.getEntityById(FileHeader.class, fileId);
740                 final FileBody body = dao.getEntityById(FileBody.class, bodyId);
741                 final User user = dao.getEntityById(User.class, userId);
742                 if (!header.hasReadPermission(user)) {
743                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
744                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
745                 }
746
747                 File f = new File(body.getStoredFilePath());
748                 try {
749                         return new FileInputStream(f);
750                 } catch (FileNotFoundException e) {
751                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
752                         throw new ObjectNotFoundException("The file contents could not be located");
753                 }
754         }
755
756         /* (non-Javadoc)
757          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFile(java.lang.Long, java.lang.Long)
758          */
759         public FileHeaderDTO getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
760                 if (userId == null)
761                         throw new ObjectNotFoundException("No user specified");
762                 if (fileId == null)
763                         throw new ObjectNotFoundException("No file specified");
764                 final User user = dao.getEntityById(User.class, userId);
765                 final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
766                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
767                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
768                 return file.getDTO();
769         }
770
771         @Override
772         public FileBodyDTO getFileBody(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
773                 if (userId == null)
774                         throw new ObjectNotFoundException("No user specified");
775                 if (fileId == null)
776                         throw new ObjectNotFoundException("No file specified");
777                 User user = dao.getEntityById(User.class, userId);
778                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
779                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
780                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
781                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
782                 return body.getDTO();
783         }
784
785         @Override
786         public Object getResourceAtPath(Long ownerId, String path, boolean ignoreDeleted)
787                         throws ObjectNotFoundException {
788                 if (ownerId == null)
789                         throw new ObjectNotFoundException("No user specified");
790                 if (StringUtils.isEmpty(path))
791                         throw new ObjectNotFoundException("No path specified");
792
793                 User owner = dao.getEntityById(User.class, ownerId);
794                 List<String> pathElements = new ArrayList<String>();
795                 StringTokenizer st = new StringTokenizer(path, "/");
796                 while (st.hasMoreTokens())
797                         pathElements.add(st.nextToken());
798                 if (pathElements.size() < 1)
799                         return getRootFolder(owner.getId());
800                 // Store the last element, since it requires special handling.
801                 String lastElement = pathElements.remove(pathElements.size() - 1);
802                 FolderDTO cursor = getRootFolder(owner.getId());
803                 // Traverse and verify the specified folder path.
804                 for (String pathElement : pathElements) {
805                         cursor = getFolder(cursor.getId(), pathElement);
806                         if (cursor.isDeleted())
807                                 throw new ObjectNotFoundException("Folder " + cursor.getPath() + " not found");
808                 }
809
810                 // Use the lastElement to retrieve the actual resource.
811                 Object resource = null;
812                 try {
813                         FileHeaderDTO file = getFile(cursor.getId(), lastElement);
814                         if (ignoreDeleted && file.isDeleted())
815                                 throw new ObjectNotFoundException("Resource not found");
816                         resource = file;
817                 } catch (ObjectNotFoundException e) {
818                         // Perhaps the requested resource is not a file, so
819                         // check for folders as well.
820                         FolderDTO folder = getFolder(cursor.getId(), lastElement);
821                         if (ignoreDeleted && folder.isDeleted())
822                                 throw new ObjectNotFoundException("Resource not found");
823                         resource = folder;
824                 }
825                 return resource;
826         }
827
828         /**
829          * Retrieve a file for the specified user that has the specified name and
830          * its parent folder has id equal to folderId.
831          *
832          * @param userId the ID of the current user
833          * @param folderId the ID of the parent folder
834          * @param name the name of the requested file
835          * @return the file found
836          * @throws ObjectNotFoundException if the specified folder or file was not
837          *             found, with the exception message mentioning the precise
838          *             problem
839          */
840         private FileHeaderDTO getFile(Long folderId, String name) throws ObjectNotFoundException {
841                 if (folderId == null)
842                         throw new ObjectNotFoundException("No parent folder specified");
843                 if (StringUtils.isEmpty(name))
844                         throw new ObjectNotFoundException("No file specified");
845
846                 FileHeader file = dao.getFile(folderId, name);
847                 return file.getDTO();
848         }
849
850         /**
851          * Retrieve a folder for the specified user that has the specified name and
852          * its parent folder has id equal to parentId.
853          *
854          * @param parentId the ID of the parent folder
855          * @param name the name of the requested folder
856          * @return the folder found
857          * @throws ObjectNotFoundException if the specified folder or parent was not
858          *             found, with the exception message mentioning the precise
859          *             problem
860          */
861         private FolderDTO getFolder(Long parentId, String name) throws ObjectNotFoundException {
862                 if (parentId == null)
863                         throw new ObjectNotFoundException("No parent folder specified");
864                 if (StringUtils.isEmpty(name))
865                         throw new ObjectNotFoundException("No folder specified");
866
867                 Folder folder = dao.getFolder(parentId, name);
868                 return folder.getDTO();
869         }
870
871         private FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, InputStream resourceInputStream) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
872                 File file = null;
873                 try {
874                         file = uploadFile(resourceInputStream, userId);
875                 } catch ( IOException ioe) {
876                         // Supply a more accurate problem description.
877                         throw new GSSIOException("Problem creating file",ioe);
878                 }
879                 return updateFileContents(userId, fileId, mimeType, file.length(), file.getAbsolutePath());
880         }
881
882         @Override
883         public void copyFile(Long userId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
884                 if (userId == null)
885                         throw new ObjectNotFoundException("No user specified");
886                 if (fileId == null)
887                         throw new ObjectNotFoundException("No file specified");
888                 if (StringUtils.isEmpty(dest))
889                         throw new ObjectNotFoundException("No destination specified");
890
891                 Object destination = getResourceAtPath(userId, getParentPath(dest), true);
892                 if (!(destination instanceof FolderDTO))
893                         throw new ObjectNotFoundException("Destination parent folder not found");
894                 FolderDTO parent = (FolderDTO) destination;
895                 copyFile(userId, fileId, parent.getId(), getLastElement(dest));
896         }
897
898         @Override
899         public void copyFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
900                 if (userId == null)
901                         throw new ObjectNotFoundException("No user specified");
902                 if (ownerId == null)
903                         throw new ObjectNotFoundException("No owner specified");
904                 if (fileId == null)
905                         throw new ObjectNotFoundException("No file specified");
906                 if (StringUtils.isEmpty(dest))
907                         throw new ObjectNotFoundException("No destination specified");
908
909                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
910                 if (!(destination instanceof FolderDTO))
911                         throw new ObjectNotFoundException("Destination parent folder not found");
912                 FolderDTO parent = (FolderDTO) destination;
913                 copyFile(userId, fileId, parent.getId(), getLastElement(dest));
914         }
915
916         @Override
917         public void copyFile(Long userId, Long fileId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
918                 if (userId == null)
919                         throw new ObjectNotFoundException("No user specified");
920                 if (fileId == null)
921                         throw new ObjectNotFoundException("No file specified");
922                 if (destId == null)
923                         throw new ObjectNotFoundException("No destination specified");
924                 if (StringUtils.isEmpty(destName))
925                         throw new ObjectNotFoundException("No destination file name specified");
926
927                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
928                 Folder destination = dao.getEntityById(Folder.class, destId);
929                 User user = dao.getEntityById(User.class, userId);
930                 if (!file.hasReadPermission(user) || !destination.hasWritePermission(user))
931                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
932                 boolean versioned = file.isVersioned();
933                 int versionsNumber = file.getBodies().size();
934                 FileBody oldestBody = file.getBodies().get(0);
935                 assert oldestBody != null;
936                 File contents = new File(oldestBody.getStoredFilePath());
937                 try {
938                         createFile(user.getId(), destination.getId(), destName, oldestBody.getMimeType(), new FileInputStream(contents));
939                         FileHeader copiedFile = dao.getFile(destination.getId(), destName);
940                         copiedFile.setVersioned(versioned);
941                         dao.flush();
942                         if (versionsNumber > 1)
943                                 for (int i = 1; i < versionsNumber; i++) {
944                                         FileBody body = file.getBodies().get(i);
945                                         assert body != null;
946                                         contents = new File(body.getStoredFilePath());
947                                         updateFileContents(user.getId(), copiedFile.getId(), body.getMimeType(), new FileInputStream(contents));
948                                 }
949                         List<FileTag> tags = file.getFileTags();
950                         for (FileTag tag : tags)
951                                 createTag(userId, copiedFile.getId(), tag.getTag());
952
953                 } catch (FileNotFoundException e) {
954                         throw new ObjectNotFoundException("File contents not found for file " + contents.getAbsolutePath());
955                 }
956
957         }
958
959         @Override
960         public void copyFolder(Long userId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
961                 if (userId == null)
962                         throw new ObjectNotFoundException("No user specified");
963                 if (folderId == null)
964                         throw new ObjectNotFoundException("No folder specified");
965                 if (StringUtils.isEmpty(dest))
966                         throw new ObjectNotFoundException("No destination specified");
967
968                 Object destination = getResourceAtPath(userId, getParentPath(dest), true);
969                 if (!(destination instanceof FolderDTO))
970                         throw new ObjectNotFoundException("Destination folder not found");
971                 FolderDTO parent = (FolderDTO) destination;
972                 copyFolder(userId, folderId, parent.getId(), getLastElement(dest));
973         }
974
975         @Override
976         public void copyFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
977                 if (userId == null)
978                         throw new ObjectNotFoundException("No user specified");
979                 if (folderId == null)
980                         throw new ObjectNotFoundException("No folder specified");
981                 if (destId == null)
982                         throw new ObjectNotFoundException("No destination specified");
983                 if (StringUtils.isEmpty(destName))
984                         throw new ObjectNotFoundException("No destination folder name specified");
985                 Folder folder = dao.getEntityById(Folder.class, folderId);
986                 Folder destination = dao.getEntityById(Folder.class, destId);
987                 User user = dao.getEntityById(User.class, userId);
988                 if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
989                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
990                 createFolder(user.getId(), destination.getId(), destName);
991         }
992
993         @Override
994         public void copyFolderStructureToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
995                 if (userId == null)
996                         throw new ObjectNotFoundException("No user specified");
997                 if (ownerId == null)
998                         throw new ObjectNotFoundException("No owner specified");
999                 if (folderId == null)
1000                         throw new ObjectNotFoundException("No folder specified");
1001                 if (StringUtils.isEmpty(dest))
1002                         throw new ObjectNotFoundException("No destination specified");
1003
1004                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1005                 if (!(destination instanceof FolderDTO))
1006                         throw new ObjectNotFoundException("Destination folder not found");
1007                 FolderDTO parent = (FolderDTO) destination;
1008                 copyFolderStructure(userId, folderId, parent.getId(), getLastElement(dest));
1009         }
1010
1011         @Override
1012         public void copyFolderStructure(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1013                 if (userId == null)
1014                         throw new ObjectNotFoundException("No user specified");
1015                 if (folderId == null)
1016                         throw new ObjectNotFoundException("No folder specified");
1017                 if (destId == null)
1018                         throw new ObjectNotFoundException("No destination specified");
1019                 if (StringUtils.isEmpty(destName))
1020                         throw new ObjectNotFoundException("No destination folder name specified");
1021
1022                 Folder folder = dao.getEntityById(Folder.class, folderId);
1023                 Folder destination = dao.getEntityById(Folder.class, destId);
1024                 final User user = dao.getEntityById(User.class, userId);
1025                 // XXX: quick fix need to copy only visible items to user (Source
1026                 // for bugs)
1027                 if (!folder.getOwner().getId().equals(userId) && !folder.hasReadPermission(user))
1028                         return;
1029                 if(folder.isDeleted())//do not copy trashed folder and contents
1030                         return;
1031                 if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
1032                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1033                 createFolder(user.getId(), destination.getId(), destName);
1034                 Folder createdFolder = dao.getFolder(destination.getId(), destName);
1035                 List<FileHeader> files = folder.getFiles();
1036                 if (files != null)
1037                         for (FileHeader file : files)
1038                                 if(!file.isDeleted())
1039                                         copyFile(userId, file.getId(), createdFolder.getId(), file.getName());
1040                 List<Folder> subFolders = folder.getSubfolders();
1041                 if (subFolders != null)
1042                         for (Folder sub : subFolders)
1043                                 if(!sub.getId().equals(createdFolder.getId()))
1044                                         copyFolderStructure(userId, sub.getId(), createdFolder.getId(), sub.getName());
1045
1046         }
1047
1048         /**
1049          * For a provided path, remove the last element and return the rest, that is
1050          * the path of the parent folder.
1051          *
1052          * @param path the specified path
1053          * @return the path of the parent folder
1054          * @throws ObjectNotFoundException if the provided string contains no path
1055          *             delimiters
1056          */
1057         private String getParentPath(String path) throws ObjectNotFoundException {
1058                 int lastDelimiter = path.lastIndexOf('/');
1059                 if (lastDelimiter == 0)
1060                         return "/";
1061                 if (lastDelimiter == -1)
1062                         // No path found.
1063                         throw new ObjectNotFoundException("There is no parent in the path: " + path);
1064                 else if (lastDelimiter < path.length() - 1)
1065                         // Return the part before the delimiter.
1066                         return path.substring(0, lastDelimiter);
1067                 else {
1068                         // Remove the trailing delimiter and then recurse.
1069                         String strippedTrail = path.substring(0, lastDelimiter);
1070                         return getParentPath(strippedTrail);
1071                 }
1072         }
1073
1074         /**
1075          * Get the last element in a path that denotes the file or folder name.
1076          *
1077          * @param path the provided path
1078          * @return the last element in the path
1079          */
1080         private String getLastElement(String path) {
1081                 int lastDelimiter = path.lastIndexOf('/');
1082                 if (lastDelimiter == -1)
1083                         // No path found.
1084                         return path;
1085                 else if (lastDelimiter < path.length() - 1)
1086                         // Return the part after the delimiter.
1087                         return path.substring(lastDelimiter + 1);
1088                 else {
1089                         // Remove the trailing delimiter and then recurse.
1090                         String strippedTrail = path.substring(0, lastDelimiter);
1091                         return getLastElement(strippedTrail);
1092                 }
1093         }
1094
1095         @Override
1096         public void moveFileToTrash(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1097                 if (userId == null)
1098                         throw new ObjectNotFoundException("No user specified");
1099                 if (fileId == null)
1100                         throw new ObjectNotFoundException("No file specified");
1101
1102                 // Do the actual work.
1103                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1104                 Folder parent = file.getFolder();
1105                 if (parent == null)
1106                         throw new ObjectNotFoundException("The specified file has no parent folder");
1107                 User user = dao.getEntityById(User.class, userId);
1108                 if (!file.hasDeletePermission(user))
1109                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1110
1111                 file.setDeleted(true);
1112                 dao.update(file);
1113                 touchParentFolders(parent, user, new Date());
1114         }
1115
1116         @Override
1117         public void moveFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
1118                 if (userId == null)
1119                         throw new ObjectNotFoundException("No user specified");
1120                 if (ownerId == null)
1121                         throw new ObjectNotFoundException("No owner specified");
1122                 if (fileId == null)
1123                         throw new ObjectNotFoundException("No file specified");
1124                 if (StringUtils.isEmpty(dest))
1125                         throw new ObjectNotFoundException("No destination specified");
1126
1127                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1128                 if (!(destination instanceof FolderDTO))
1129                         throw new ObjectNotFoundException("Destination parent folder not found");
1130                 FolderDTO parent = (FolderDTO) destination;
1131                 moveFile(userId, fileId, parent.getId(), getLastElement(dest));
1132         }
1133
1134         @Override
1135         public void moveFile(Long userId, Long fileId, Long destId, String destName) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1136                 if (userId == null)
1137                         throw new ObjectNotFoundException("No user specified");
1138                 if (fileId == null)
1139                         throw new ObjectNotFoundException("No file specified");
1140                 if (destId == null)
1141                         throw new ObjectNotFoundException("No destination specified");
1142                 if (StringUtils.isEmpty(destName))
1143                         throw new ObjectNotFoundException("No destination file name specified");
1144
1145                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1146                 Folder source = file.getFolder();
1147                 Folder destination = dao.getEntityById(Folder.class, destId);
1148
1149                 User owner = dao.getEntityById(User.class, userId);
1150                 if (!file.hasDeletePermission(owner) || !destination.hasWritePermission(owner))
1151                         throw new InsufficientPermissionsException("User " + owner.getId() + " cannot move file " + file.getName() + "(" + file.getId() + ")");
1152
1153                 // if the destination folder belongs to another user:
1154                 if (!file.getOwner().equals(destination.getOwner())) {
1155                         // (a) check if the destination quota allows the move
1156                         if(getQuotaLeft(destination.getOwner().getId()) < file.getTotalSize())
1157                                 throw new QuotaExceededException("Not enough free space available");
1158                         User newOwner = destination.getOwner();
1159                         // (b) if quota OK, change the owner of the file
1160                         file.setOwner(newOwner);
1161                         // if the file has no permission for the new owner, add it
1162                         Permission ownerPermission = null;
1163                         for (final Permission p : file.getPermissions())
1164                                 if (p.getUser() != null)
1165                                         if (p.getUser().equals(newOwner)) {
1166                                                 ownerPermission = p;
1167                                                 break;
1168                                         }
1169                         if (ownerPermission == null) {
1170                                 ownerPermission = new Permission();
1171                                 ownerPermission.setUser(newOwner);
1172                                 file.addPermission(ownerPermission);
1173                         }
1174                         ownerPermission.setRead(true);
1175                         ownerPermission.setWrite(true);
1176                         ownerPermission.setModifyACL(true);
1177                 }
1178                 // move the file to the destination folder
1179                 file.setFolder(destination);
1180                 touchParentFolders(source, owner, new Date());
1181                 touchParentFolders(destination, owner, new Date());
1182         }
1183
1184         @Override
1185         public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1186                 if (userId == null)
1187                         throw new ObjectNotFoundException("No user specified");
1188                 if (ownerId == null)
1189                         throw new ObjectNotFoundException("No owner specified");
1190                 if (folderId == null)
1191                         throw new ObjectNotFoundException("No folder specified");
1192                 if (StringUtils.isEmpty(dest))
1193                         throw new ObjectNotFoundException("No destination specified");
1194
1195                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1196                 if (!(destination instanceof FolderDTO))
1197                         throw new ObjectNotFoundException("Destination parent folder not found");
1198                 FolderDTO parent = (FolderDTO) destination;
1199                 moveFolder(userId, folderId, parent.getId(), getLastElement(dest));
1200         }
1201
1202         @Override
1203         public void moveFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1204                 // TODO Simple Move and delete of original folder, in production
1205                 // scenario we must first check individual files and folders permissions
1206                 copyFolderStructure(userId, folderId, destId, destName);
1207                 deleteFolder(userId, folderId);
1208         }
1209
1210         /* (non-Javadoc)
1211          * @see gr.ebs.gss.server.ejb.ExternalAPI#getDeletedFiles(java.lang.Long)
1212          */
1213         public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
1214                 // Validate.
1215                 if (userId == null)
1216                         throw new ObjectNotFoundException("No user specified");
1217
1218                 // Do the actual work.
1219                 final List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1220                 final List<FileHeader> files = dao.getDeletedFiles(userId);
1221                 for (final FileHeader f : files)
1222                         result.add(f.getDTO());
1223                 return result;
1224         }
1225
1226         @Override
1227         public void removeFileFromTrash(Long userId, Long fileId)
1228                         throws ObjectNotFoundException, InsufficientPermissionsException {
1229                 if (userId == null)
1230                         throw new ObjectNotFoundException("No user specified");
1231                 if (fileId == null)
1232                         throw new ObjectNotFoundException("No file specified");
1233
1234                 // Do the actual work.
1235                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1236                 Folder parent = file.getFolder();
1237                 if (parent == null)
1238                         throw new ObjectNotFoundException("The specified file has no parent folder");
1239                 User user = dao.getEntityById(User.class, userId);
1240                 if (!file.hasDeletePermission(user))
1241                         throw new InsufficientPermissionsException("User " + user.getUsername() +
1242                                                 " cannot restore file " + file.getName());
1243
1244                 file.setDeleted(false);
1245                 dao.update(file);
1246                 touchParentFolders(parent, user, new Date());
1247         }
1248
1249         @Override
1250         public void moveFolderToTrash(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1251                 if (userId == null)
1252                         throw new ObjectNotFoundException("No user specified");
1253                 if (folderId == null)
1254                         throw new ObjectNotFoundException("No folder specified");
1255                 Folder folder = dao.getEntityById(Folder.class, folderId);
1256                 User user = dao.getEntityById(User.class, userId);
1257                 if (!folder.hasDeletePermission(user))
1258                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1259                 folder.setDeleted(true);
1260                 dao.update(folder);
1261                 touchParentFolders(folder, user, new Date());
1262                 for (FileHeader file : folder.getFiles())
1263                         moveFileToTrash(userId, file.getId());
1264                 for (Folder subFolder : folder.getSubfolders())
1265                         moveFolderToTrash(userId, subFolder.getId());
1266
1267         }
1268
1269         @Override
1270         public void removeFolderFromTrash(Long userId, Long folderId)
1271                         throws ObjectNotFoundException, InsufficientPermissionsException {
1272                 if (userId == null)
1273                         throw new ObjectNotFoundException("No user specified");
1274                 if (folderId == null)
1275                         throw new ObjectNotFoundException("No folder specified");
1276                 Folder folder = dao.getEntityById(Folder.class, folderId);
1277                 User user = dao.getEntityById(User.class, userId);
1278                 if (!folder.hasDeletePermission(user))
1279                         throw new InsufficientPermissionsException("User " + user.getUsername() +
1280                                                 " cannot restore folder " + folder.getName());
1281                 folder.setDeleted(false);
1282                 for (FileHeader file : folder.getFiles())
1283                         removeFileFromTrash(userId, file.getId());
1284                 for (Folder subFolder : folder.getSubfolders())
1285                         removeFolderFromTrash(userId, subFolder.getId());
1286                 dao.update(folder);
1287                 touchParentFolders(folder, user, new Date());
1288         }
1289
1290         @Override
1291         public List<FolderDTO> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
1292                 List<Folder> folders = dao.getDeletedRootFolders(userId);
1293                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1294                 for (Folder folder : folders)
1295                         result.add(folder.getDTO());
1296                 return result;
1297         }
1298
1299         @Override
1300         public void emptyTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1301                 List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1302                 for (FolderDTO fdto : deletedRootFolders)
1303                         deleteFolder(userId, fdto.getId());
1304                 List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1305                 for (FileHeaderDTO filedto : deletedFiles)
1306                         deleteFile(userId, filedto.getId());
1307         }
1308
1309         @Override
1310         public void restoreTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1311                 List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1312                 for (FolderDTO fdto : deletedRootFolders)
1313                         removeFolderFromTrash(userId, fdto.getId());
1314                 List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1315                 for (FileHeaderDTO filedto : deletedFiles)
1316                         removeFileFromTrash(userId, filedto.getId());
1317         }
1318
1319         @Override
1320         public User createUser(String username, String name, String mail) throws ObjectNotFoundException {
1321                 if (username == null)
1322                         throw new ObjectNotFoundException("No username specified");
1323                 if (name == null)
1324                         throw new ObjectNotFoundException("No name specified");
1325
1326                 User user = new User();
1327                 user.setUsername(username);
1328                 user.setName(name);
1329                 user.setEmail(mail);
1330                 Date now = new Date();
1331                 AuditInfo auditInfo = new AuditInfo();
1332                 auditInfo.setCreationDate(now);
1333                 auditInfo.setModificationDate(now);
1334                 user.setAuditInfo(auditInfo);
1335                 user.generateAuthToken();
1336                 user.generateWebDAVPassword();
1337                 dao.create(user);
1338                 // Make sure we get an ID in the user object.
1339                 dao.flush();
1340                 // Create the root folder for the user.
1341                 createFolder(user.getName(), null, user);
1342                 return user;
1343         }
1344
1345         @Override
1346         public User findUserByEmail(String email) {
1347                 return dao.findUserByEmail(email);
1348         }
1349
1350         @Override
1351         public void updateUser(User user) {
1352                 dao.update(user);
1353         }
1354
1355         @Override
1356         public User updateUser(String username, String name, String mail) throws ObjectNotFoundException {
1357                 if (username == null)
1358                         throw new ObjectNotFoundException("No username specified");
1359
1360                 User user = dao.getUser(username);
1361                 user.setName(name);
1362                 user.setEmail(mail);
1363                 return user;
1364         }
1365
1366         @Override
1367         public User findUser(String username) {
1368                 if (username == null)
1369                         return null;
1370                 return dao.findUser(username);
1371         }
1372
1373         @Override
1374         public User updateUserToken(Long userId) throws ObjectNotFoundException {
1375                 if (userId == null)
1376                         throw new ObjectNotFoundException("No user specified");
1377                 User user = dao.getEntityById(User.class, userId);
1378                 user.generateAuthToken();
1379                 return user;
1380         }
1381
1382         /* (non-Javadoc)
1383          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolderPermissions(java.lang.Long, java.lang.Long)
1384          */
1385         @Override
1386         public Set<PermissionDTO> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1387                 if (userId == null)
1388                         throw new ObjectNotFoundException("No user specified");
1389                 if (folderId == null)
1390                         throw new ObjectNotFoundException("No folder specified");
1391                 User user = dao.getEntityById(User.class, userId);
1392                 Folder folder = dao.getEntityById(Folder.class, folderId);
1393                 if(!folder.hasReadPermission(user))
1394                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1395                 Set<Permission> perms = folder.getPermissions();
1396                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1397                 for (Permission perm : perms)
1398                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1399                                 result.add(perm.getDTO());
1400                 for (Permission perm : perms)
1401                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1402                         } else
1403                                 result.add(perm.getDTO());
1404                 return result;
1405
1406         }
1407
1408         /* (non-Javadoc)
1409          * @see gr.ebs.gss.server.ejb.ExternalAPI#setFolderPermissions(java.lang.Long, java.lang.Long, java.util.Set)
1410          */
1411         @Override
1412         public void setFolderPermissions(Long userId, Long folderId, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1413                 if (userId == null)
1414                         throw new ObjectNotFoundException("No user specified");
1415                 if (folderId == null)
1416                         throw new ObjectNotFoundException("No folder specified");
1417                 User user = dao.getEntityById(User.class, userId);
1418                 Folder folder = dao.getEntityById(Folder.class, folderId);
1419                 if(!folder.hasModifyACLPermission(user))
1420                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1421                 // Delete previous entries
1422                 for (Permission perm: folder.getPermissions())
1423                         dao.delete(perm);
1424                 folder.getPermissions().clear();
1425                 for (PermissionDTO dto : permissions) {
1426                         if (dto.getUser()!=null && dto.getUser().getId().equals(folder.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
1427                                         throw new InsufficientPermissionsException("Can't remove permissions from owner");
1428                         // Don't include 'empty' permission
1429                         if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1430                         folder.addPermission(getPermission(dto));
1431                 }
1432                 dao.update(folder);
1433                 for (FileHeader fh : folder.getFiles())
1434                         setFilePermissions(userId, fh.getId(), fh.isReadForAll(), permissions);
1435                 for (Folder sub : folder.getSubfolders())
1436                         setFolderPermissions(userId, sub.getId(), permissions);
1437         }
1438
1439         private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
1440                 Permission res = new Permission();
1441                 if (dto.getGroup() != null)
1442                         res.setGroup(dao.getEntityById(Group.class, dto.getGroup().getId()));
1443                 else if (dto.getUser() != null)
1444                         if (dto.getUser().getId() == null)
1445                                 res.setUser(dao.getUser(dto.getUser().getUsername()));
1446                         else
1447                                 res.setUser(dao.getEntityById(User.class, dto.getUser().getId()));
1448                 res.setRead(dto.hasRead());
1449                 res.setWrite(dto.hasWrite());
1450                 res.setModifyACL(dto.hasModifyACL());
1451                 return res;
1452         }
1453
1454         /* (non-Javadoc)
1455          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
1456          */
1457         @Override
1458         public List<UserDTO> getUsersByUserNameLike(String username) {
1459                 List<User> users = dao.getUsersByUserNameLike(username);
1460                 List<UserDTO> result = new ArrayList<UserDTO>();
1461                 for (User u : users)
1462                         result.add(u.getDTO());
1463                 return result;
1464
1465         }
1466
1467         /* (non-Javadoc)
1468          * @see gr.ebs.gss.server.ejb.ExternalAPI#addUserToGroup(java.lang.Long, java.lang.Long, java.lang.Long)
1469          */
1470         @Override
1471         public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1472                 if (userId == null)
1473                         throw new ObjectNotFoundException("No user specified");
1474                 if (groupId == null)
1475                         throw new ObjectNotFoundException("No group specified");
1476                 if (userToAddId == null)
1477                         throw new ObjectNotFoundException("No user to add specified");
1478                 User user = dao.getEntityById(User.class, userId);
1479                 Group group = dao.getEntityById(Group.class, groupId);
1480                 if (!group.getOwner().equals(user))
1481                         throw new InsufficientPermissionsException();
1482                 User userToAdd = dao.getEntityById(User.class, userToAddId);
1483                 if (group.contains(userToAdd))
1484                         throw new DuplicateNameException("User already exists in group");
1485                 group.getMembers().add(userToAdd);
1486                 dao.update(group);
1487
1488         }
1489
1490         @Override
1491         public void invalidateUserToken(Long userId) throws ObjectNotFoundException {
1492                 if (userId == null)
1493                         throw new ObjectNotFoundException("No user specified");
1494                 User user = dao.getEntityById(User.class, userId);
1495                 user.invalidateAuthToken();
1496                 return;
1497         }
1498
1499         @Override
1500         public List<FolderDTO> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
1501                 if (userId == null)
1502                         throw new ObjectNotFoundException("No user specified");
1503                 List<Folder> folders = dao.getSharedRootFolders(userId);
1504                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1505                 for (Folder f : folders) {
1506                         FolderDTO dto = f.getDTO();
1507                         dto.setSubfolders(getSharedSubfolders(userId, f.getId()));
1508                         result.add(dto);
1509                 }
1510                 return result;
1511         }
1512
1513         /* (non-Javadoc)
1514          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeMemberFromGroup(java.lang.Long, java.lang.Long, java.lang.Long)
1515          */
1516         @Override
1517         public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
1518                 if (userId == null)
1519                         throw new ObjectNotFoundException("No user specified");
1520                 if (groupId == null)
1521                         throw new ObjectNotFoundException("No group specified");
1522                 if (memberId == null)
1523                         throw new ObjectNotFoundException("No member specified");
1524                 User owner = dao.getEntityById(User.class, userId);
1525                 Group group = dao.getEntityById(Group.class, groupId);
1526                 User member = dao.getEntityById(User.class, memberId);
1527                 if (!group.getOwner().equals(owner))
1528                         throw new InsufficientPermissionsException("User is not the owner of the group");
1529                 group.removeMemberFromGroup(member);
1530                 dao.update(group);
1531
1532         }
1533
1534         /* (non-Javadoc)
1535          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersSharingFoldersForUser(java.lang.Long)
1536          */
1537         @Override
1538         public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
1539                 List<User> users = dao.getUsersSharingFoldersForUser(userId);
1540                 List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
1541                 List<UserDTO> res = new ArrayList<UserDTO>();
1542                 for (User u : users)
1543                         res.add(u.getDTO());
1544                 for(User fu : usersFiles)
1545                         if(!users.contains(fu))
1546                                 res.add(fu.getDTO());
1547                 return res;
1548         }
1549
1550         /* (non-Javadoc)
1551          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFilePermissions(java.lang.Long, java.lang.Long)
1552          */
1553         @Override
1554         public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1555                 if (userId == null)
1556                         throw new ObjectNotFoundException("No user specified");
1557                 if (fileId == null)
1558                         throw new ObjectNotFoundException("No folder specified");
1559                 User user = dao.getEntityById(User.class, userId);
1560                 FileHeader folder = dao.getEntityById(FileHeader.class, fileId);
1561                 if(!folder.hasReadPermission(user))
1562                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1563                 Set<Permission> perms = folder.getPermissions();
1564                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1565                 for (Permission perm : perms)
1566                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1567                                 result.add(perm.getDTO());
1568                 for (Permission perm : perms)
1569                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1570                         } else
1571                                 result.add(perm.getDTO());
1572                 return result;
1573         }
1574
1575         @Override
1576         public void setFilePermissions(Long userId, Long fileId, Boolean readForAll, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1577                 if (userId == null)
1578                         throw new ObjectNotFoundException("No user specified");
1579                 if (fileId == null)
1580                         throw new ObjectNotFoundException("No folder specified");
1581
1582                 User user = dao.getEntityById(User.class, userId);
1583                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1584                 if(!file.hasModifyACLPermission(user))
1585                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1586
1587                 if (readForAll != null)
1588                         if (user.equals(file.getOwner()))
1589                                 file.setReadForAll(readForAll);
1590                         else
1591                                 throw new InsufficientPermissionsException("Only the owner can change the read-for-all flag");
1592
1593                 // Update the file if there was a change.
1594                 if (readForAll != null || permissions != null && !permissions.isEmpty()) {
1595                         if (permissions != null && !permissions.isEmpty()) {
1596                                 // Delete previous entries
1597                                 for (Permission perm: file.getPermissions())
1598                                         dao.delete(perm);
1599                                 file.getPermissions().clear();
1600                                 for (PermissionDTO dto : permissions) {
1601                                         if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
1602                                                 throw new InsufficientPermissionsException("Can't remove permissions from owner");
1603                                         // Don't include 'empty' permission
1604                                         if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1605                                         file.addPermission(getPermission(dto));
1606                                 }
1607                         }
1608
1609                         dao.update(file);
1610                         Folder parent = file.getFolder();
1611                         touchParentFolders(parent, user, new Date());
1612                 }
1613         }
1614
1615         @Override
1616         public List<FileHeaderDTO> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1617                 if (userId == null)
1618                         throw new ObjectNotFoundException("No user specified");
1619                 List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1620                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1621                 for (FileHeader f : files)
1622                         result.add(f.getDTO());
1623                 return result;
1624         }
1625
1626         @Override
1627         public List<FileHeaderDTO> getSharedFiles(Long userId) throws ObjectNotFoundException {
1628                 if (userId == null)
1629                         throw new ObjectNotFoundException("No user specified");
1630                 List<FileHeader> files = dao.getSharedFiles(userId);
1631                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1632                 for (FileHeader f : files)
1633                         result.add(f.getDTO());
1634                 return result;
1635         }
1636
1637         @Override
1638         public List<FolderDTO> getSharedFolders(Long userId) throws ObjectNotFoundException {
1639                 if (userId == null)
1640                         throw new ObjectNotFoundException("No user specified");
1641                 List<Folder> folders = dao.getSharedFolders(userId);
1642                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1643                 for (Folder f : folders)
1644                         result.add(f.getDTO());
1645                 return result;
1646         }
1647
1648         /* (non-Javadoc)
1649          * @see gr.ebs.gss.server.ejb.ExternalAPI#getSharedFiles(java.lang.Long, java.lang.Long)
1650          */
1651         @Override
1652         public List<FileHeaderDTO> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1653                 if (ownerId == null)
1654                         throw new ObjectNotFoundException("No owner specified");
1655                 if (callingUserId == null)
1656                         throw new ObjectNotFoundException("No calling user specified");
1657                 List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1658                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1659                 for (FileHeader f : folders)
1660                         result.add(f.getDTO());
1661                 return result;
1662         }
1663
1664         @Override
1665         public List<FolderDTO> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1666                 if (ownerId == null)
1667                         throw new ObjectNotFoundException("No owner specified");
1668                 if (callingUserId == null)
1669                         throw new ObjectNotFoundException("No calling user specified");
1670                 List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
1671                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1672                 for (Folder f : folders) {
1673                         FolderDTO dto = f.getDTO();
1674                         dto.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
1675                         result.add(dto);
1676                 }
1677                 return result;
1678
1679         }
1680
1681         @Override
1682         public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1683                 if (userId == null)
1684                         throw new ObjectNotFoundException("No user specified");
1685                 if (folderId == null)
1686                         throw new ObjectNotFoundException("No folder specified");
1687                 User user = dao.getEntityById(User.class, userId);
1688                 Folder folder = dao.getEntityById(Folder.class, folderId);
1689                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1690                 if (folder.isShared(user))
1691                         for (Folder f : folder.getSubfolders())
1692                                 if (f.isShared(user) && !f.isDeleted())
1693                                         result.add(f.getDTO());
1694                 return result;
1695         }
1696
1697         @Override
1698         public List<FolderDTO> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
1699                 if (userId == null)
1700                         throw new ObjectNotFoundException("No user specified");
1701                 if (callingUserId == null)
1702                         throw new ObjectNotFoundException("No user specified");
1703                 if (folderId == null)
1704                         throw new ObjectNotFoundException("No folder specified");
1705                 User user = dao.getEntityById(User.class, callingUserId);
1706                 Folder folder = dao.getEntityById(Folder.class, folderId);
1707                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1708                 if (folder.isSharedForOtherUser(user))
1709                         for (Folder f : folder.getSubfolders())
1710                                 if (f.isSharedForOtherUser(user) && !f.isDeleted()){
1711                                         FolderDTO dto = f.getDTO();
1712                                         dto.setSubfolders(getSharedSubfolders(userId, callingUserId, dto.getId()));
1713                                         result.add(dto);
1714                                 }
1715                 return result;
1716
1717         }
1718
1719         /* (non-Javadoc)
1720          * @see gr.ebs.gss.server.ejb.ExternalAPI#searchFiles(java.lang.Long, java.lang.String)
1721          */
1722         @Override
1723         public List<FileHeaderDTO> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1724                 if (userId == null)
1725                         throw new ObjectNotFoundException("No user specified");
1726                 User user = getUser(userId);
1727                 if (query == null)
1728                         throw new ObjectNotFoundException("No query specified");
1729                 List<FileHeader> files = search(user.getId(), query);
1730                 List<FileHeaderDTO> res = new ArrayList<FileHeaderDTO>();
1731                 for(FileHeader f : files)
1732                         res.add(f.getDTO());
1733                 return res;
1734         }
1735
1736         /**
1737          * Performs the actuals search on the solr server and returns the results
1738          *
1739          * We have to use the dismax query type (instead of the
1740          * standard) because it allows for search time field boosting. This is because we can't use indexing
1741          * time field boosting due to the patched rich indexing API that does not allow it
1742          *
1743          * @param userId
1744          * @param query
1745          * @return a List of FileHeader objects
1746          */
1747         private List<FileHeader> search(Long userId, String query) {
1748                 try {
1749                         HttpClient httpClient = new HttpClient();
1750
1751                         GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl"));
1752                         NameValuePair[] params = {new NameValuePair("qt", "dismax"),
1753                                                                                 new NameValuePair("q", query),
1754                                                                                 new NameValuePair("sort", "score desc"),
1755                                                                                 new NameValuePair("indent", "on")};
1756                         method.setQueryString(params);
1757                         int retryCount = 0;
1758                         int statusCode = 0;
1759                         String response = null;
1760                         do {
1761                                 statusCode = httpClient.executeMethod(method);
1762                                 logger.debug("HTTP status: " + statusCode);
1763                                 response = method.getResponseBodyAsString();
1764                                 logger.debug(response);
1765                                 retryCount++;
1766                                 if (statusCode != 200 && retryCount < 3)
1767                                         try {
1768                                                 Thread.sleep(3000); //Give Solr a little time to be available
1769                                         } catch (InterruptedException e) {
1770                                         }
1771                         } while (statusCode != 200 && retryCount < 3);
1772                         if (statusCode != 200)
1773                                 throw new EJBException("Search query return error:\n" + response);
1774
1775                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1776                         DocumentBuilder db = dbf.newDocumentBuilder();
1777                         Document doc = db.parse(method.getResponseBodyAsStream());
1778                         method.releaseConnection();
1779
1780                         Node root = doc.getElementsByTagName("response").item(0);
1781                         Node lst = root.getFirstChild().getNextSibling();
1782                         Node status = lst.getFirstChild().getNextSibling();
1783                         if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") &&
1784                                 status.getTextContent().equals("0")) {
1785                                 List<FileHeader> fileResult = new ArrayList<FileHeader>();
1786                                 Node result = lst.getNextSibling().getNextSibling();
1787                                 NodeList docs = result.getChildNodes();
1788                                 User user = getUser(userId);
1789                                 for (int i=1; i<docs.getLength(); i=i+2) {
1790                                         Node d = docs.item(i);
1791                                         NodeList docData = d.getChildNodes();
1792                                         for (int j=1; j<docData.getLength(); j=j+2) {
1793                                                 Node dd = docData.item(j);
1794                                                 if (dd.getAttributes().item(0).getNodeName().equals("name") &&
1795                                                         dd.getAttributes().item(0).getNodeValue().equals("id")) {
1796                                                         Long fileId = Long.valueOf(dd.getTextContent());
1797                                                         try {
1798                                                                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1799                                                                 if (file.hasReadPermission(user)) {
1800                                                                         fileResult.add(file);
1801                                                                         logger.debug("File added " + fileId);
1802                                                                 }
1803                                                         } catch (ObjectNotFoundException e) {
1804                                                                 logger.warn("Search result not found", e);
1805                                                         }
1806                                                 }
1807                                         }
1808                                 }
1809                                 return fileResult;
1810                         }
1811                         throw new EJBException();
1812                 } catch (HttpException e) {
1813                         throw new EJBException(e);
1814                 } catch (IOException e) {
1815                         throw new EJBException(e);
1816                 } catch (SAXException e) {
1817                         throw new EJBException(e);
1818                 } catch (ParserConfigurationException e) {
1819                         throw new EJBException(e);
1820                 } catch (ObjectNotFoundException e) {
1821                         throw new EJBException(e);
1822                 }
1823         }
1824
1825         /* (non-Javadoc)
1826          * @see gr.ebs.gss.server.ejb.ExternalAPI#copyFiles(java.lang.Long, java.util.List, java.lang.Long)
1827          */
1828         @Override
1829         public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1830                 for(Long l : fileIds){
1831                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1832                         copyFile(userId, l, destId, file.getName());
1833                 }
1834
1835
1836         }
1837
1838         /* (non-Javadoc)
1839          * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFiles(java.lang.Long, java.util.List, java.lang.Long)
1840          */
1841         @Override
1842         public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1843                 for(Long l : fileIds){
1844                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1845                         moveFile(userId, l, destId, file.getName());
1846                 }
1847
1848         }
1849
1850         /* (non-Javadoc)
1851          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFiles(java.lang.Long, java.util.List)
1852          */
1853         @Override
1854         public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1855                 if (userId == null)
1856                         throw new ObjectNotFoundException("No user specified");
1857                 final User user = dao.getEntityById(User.class, userId);
1858                 List<String> filesToRemove = new ArrayList<String>();
1859                 //first delete database objects
1860                 for(Long fileId : fileIds){
1861                         if (fileId == null)
1862                                 throw new ObjectNotFoundException("No file specified");
1863                         final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1864                         final Folder parent = file.getFolder();
1865                         if (parent == null)
1866                                 throw new ObjectNotFoundException("The specified file has no parent folder");
1867                         if (!file.hasDeletePermission(user))
1868                                 throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1869
1870                         parent.removeFile(file);
1871                         for (final FileBody body : file.getBodies())
1872                                 filesToRemove.add(body.getStoredFilePath());
1873                         dao.delete(file);
1874                         touchParentFolders(parent, user, new Date());
1875                 }
1876                 //then remove physical files if everything is ok
1877                 for(String physicalFileName : filesToRemove)
1878                         deleteActualFile(physicalFileName);
1879                 //then unindex deleted files
1880                 for(Long fileId : fileIds)
1881                         indexFile(fileId, true);
1882
1883         }
1884
1885         /* (non-Javadoc)
1886          * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFilesToTrash(java.lang.Long, java.util.List)
1887          */
1888         @Override
1889         public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1890                 for(Long l : fileIds)
1891                         moveFileToTrash(userId, l);
1892
1893         }
1894
1895         @Override
1896         public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1897                 for(Long l : fileIds)
1898                         removeFileFromTrash(userId, l);
1899
1900         }
1901
1902         @Override
1903         public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1904                 if (userId == null)
1905                         throw new ObjectNotFoundException("No user specified");
1906                 User user = dao.getEntityById(User.class, userId);
1907                 Nonce nonce = Nonce.createNonce(user.getId());
1908                 dao.create(nonce);
1909                 return nonce;
1910         }
1911
1912         @Override
1913         public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1914                 if (userId == null)
1915                         throw new ObjectNotFoundException("No user specified");
1916                 if (nonce == null)
1917                         throw new ObjectNotFoundException("No nonce specified");
1918                 return dao.getNonce(nonce, userId);
1919         }
1920
1921         @Override
1922         public void removeNonce(Long id) throws ObjectNotFoundException {
1923                 if (id == null)
1924                         throw new ObjectNotFoundException("No nonce specified");
1925                 Nonce nonce = dao.getEntityById(Nonce.class, id);
1926                 dao.delete(nonce);
1927         }
1928
1929         @Override
1930         public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1931                 if (userId == null)
1932                         throw new ObjectNotFoundException("No user specified");
1933                 User user = dao.getEntityById(User.class, userId);
1934                 user.setNonce(nonce);
1935                 user.setNonceExpiryDate(nonceExpiryDate);
1936         }
1937
1938         /* (non-Javadoc)
1939          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserStatistics(java.lang.Long)
1940          */
1941         @Override
1942         public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
1943                 if (userId == null)
1944                         throw new ObjectNotFoundException("No user specified");
1945                 StatsDTO stats = new StatsDTO();
1946                 stats.setFileCount(dao.getFileCount(userId));
1947                 Long fileSize = dao.getFileSize(userId);
1948                 stats.setFileSize(fileSize);
1949                 Long quota = getQuota(userId);
1950                 Long quotaLeft = quota - fileSize;
1951                 stats.setQuotaLeftSize(quotaLeft);
1952                 return stats;
1953         }
1954
1955         /* (non-Javadoc)
1956          * @see gr.ebs.gss.server.ejb.ExternalAPI#getVersions(java.lang.Long, java.lang.Long)
1957          */
1958         @Override
1959         public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1960                 if (userId == null)
1961                         throw new ObjectNotFoundException("No user specified");
1962                 if (fileId == null)
1963                         throw new ObjectNotFoundException("No file specified");
1964                 User user = dao.getEntityById(User.class, userId);
1965                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1966                 if(!header.hasReadPermission(user))
1967                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1968                 List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
1969                 for(int i = header.getBodies().size()-1 ; i>=0; i--)
1970                         result.add(header.getBodies().get(i).getDTO());
1971                 return result;
1972         }
1973
1974         /* (non-Javadoc)
1975          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeVersion(java.lang.Long, java.lang.Long, java.lang.Long)
1976          */
1977         @Override
1978         public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
1979                 if (userId == null)
1980                         throw new ObjectNotFoundException("No user specified");
1981                 if (fileId == null)
1982                         throw new ObjectNotFoundException("No file specified");
1983                 if (bodyId == null)
1984                         throw new ObjectNotFoundException("No body specified");
1985                 User user = dao.getEntityById(User.class, userId);
1986                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1987                 if(!header.hasWritePermission(user))
1988                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1989                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
1990                 if(body.equals(header.getCurrentBody())){
1991
1992                         if(header.getBodies().size() == 1)
1993                                 throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
1994                         for(FileBody b : header.getBodies())
1995                                 if(b.getVersion() == body.getVersion()-1)
1996                                         header.setCurrentBody(b);
1997                 }
1998                 deleteActualFile(body.getStoredFilePath());
1999                 header.getBodies().remove(body);
2000
2001                 Folder parent = header.getFolder();
2002                 touchParentFolders(parent, user, new Date());
2003
2004         }
2005
2006         @Override
2007         public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
2008                 if (userId == null)
2009                         throw new ObjectNotFoundException("No user specified");
2010                 if (fileId == null)
2011                         throw new ObjectNotFoundException("No file specified");
2012                 User user = dao.getEntityById(User.class, userId);
2013                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2014                 if(!header.hasWritePermission(user))
2015                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2016                 FileBody body = dao.getFileVersion(fileId, version);
2017                 final File fileContents = new File(body.getStoredFilePath());
2018
2019                 try {
2020                         updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2021                 } catch (FileNotFoundException e) {
2022                         throw new GSSIOException(e);
2023                 }
2024
2025         }
2026
2027         /* (non-Javadoc)
2028          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
2029          */
2030         @Override
2031         public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2032                 if (userId == null)
2033                         throw new ObjectNotFoundException("No user specified");
2034                 if (fileId == null)
2035                         throw new ObjectNotFoundException("No file specified");
2036                 User user = dao.getEntityById(User.class, userId);
2037                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2038                 if(!header.hasWritePermission(user))
2039                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2040                 Iterator<FileBody> it = header.getBodies().iterator();
2041                 while(it.hasNext()){
2042                         FileBody body = it.next();
2043                         if(!body.equals(header.getCurrentBody())){
2044                                 deleteActualFile(body.getStoredFilePath());
2045                                 it.remove();
2046                                 dao.delete(body);
2047                         }
2048                 }
2049                 header.getCurrentBody().setVersion(1);
2050
2051                 Folder parent = header.getFolder();
2052                 touchParentFolders(parent, user, new Date());
2053         }
2054
2055         /* (non-Javadoc)
2056          * @see gr.ebs.gss.server.ejb.ExternalAPI#toggleFileVersioning(java.lang.Long, java.lang.Long, boolean)
2057          */
2058         @Override
2059         public void toggleFileVersioning(Long userId, Long fileId, boolean versioned) throws ObjectNotFoundException, InsufficientPermissionsException {
2060                 if (userId == null)
2061                         throw new ObjectNotFoundException("No user specified");
2062                 if (fileId == null)
2063                         throw new ObjectNotFoundException("No file specified");
2064                 User user = dao.getEntityById(User.class, userId);
2065                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2066                 if(!header.hasWritePermission(user))
2067                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2068                 if(!header.isVersioned() == versioned){
2069                         if(header.isVersioned())
2070                                 removeOldVersions(userId, fileId);
2071                         header.setVersioned(versioned);
2072                         Folder parent = header.getFolder();
2073                         touchParentFolders(parent, user, new Date());
2074                 }
2075         }
2076
2077         /**
2078          * Gets the quota left for specified userId
2079          * @param userId
2080          * @return
2081          */
2082         private Long getQuotaLeft(Long userId){
2083                 Long fileSize = dao.getFileSize(userId);
2084                 Long quota = getQuota(userId);
2085                 return quota - fileSize;
2086         }
2087
2088         /**
2089          * Gets the quota for specified userId
2090          * @param userId
2091          * @return
2092          */
2093         private Long getQuota(@SuppressWarnings("unused") Long userId){
2094                 Long quota = getConfiguration().getLong("quota", new Long(52428800L));
2095                 return quota;
2096         }
2097
2098         public void rebuildSolrIndex() {
2099                 MessageProducer sender = null;
2100                 Session session = null;
2101                 Connection qConn = null;
2102                 try {
2103                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2104                         DocumentBuilder db = dbf.newDocumentBuilder();
2105                         Document doc = db.newDocument();
2106                         Node root = doc.createElement("delete");
2107                         doc.appendChild(root);
2108                         Node queryNode = doc.createElement("query");
2109                         root.appendChild(queryNode);
2110                         queryNode.appendChild(doc.createTextNode("*:*"));
2111
2112                         TransformerFactory fact = TransformerFactory.newInstance();
2113                         Transformer trans = fact.newTransformer();
2114                         trans.setOutputProperty(OutputKeys.INDENT, "yes");
2115                         StringWriter sw = new StringWriter();
2116                         StreamResult sr = new StreamResult(sw);
2117                         DOMSource source = new DOMSource(doc);
2118                         trans.transform(source, sr);
2119                         logger.debug(sw.toString());
2120
2121                         HttpClient httpClient = new HttpClient();
2122                         PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2123                         method.setRequestEntity(new StringRequestEntity(sw.toString()));
2124                         int retryCount = 0;
2125                         int statusCode = 0;
2126                         String response = null;
2127                         do {
2128                                 statusCode = httpClient.executeMethod(method);
2129                                 logger.debug("HTTP status: " + statusCode);
2130                                 response = method.getResponseBodyAsString();
2131                                 logger.debug(response);
2132                                 retryCount++;
2133                                 if (statusCode != 200 && retryCount < 3)
2134                                         try {
2135                                                 Thread.sleep(10000); //Give Solr a little time to be available
2136                                         } catch (InterruptedException e) {
2137                                         }
2138                         } while (statusCode != 200 && retryCount < 3);
2139                         method.releaseConnection();
2140                         if (statusCode != 200)
2141                                 throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response);
2142                         List<Long> fileIds = dao.getAllFileIds();
2143
2144                         Context jndiCtx = new InitialContext();
2145                         ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
2146                         Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
2147                         qConn = factory.createConnection();
2148                         session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
2149                         sender = session.createProducer(queue);
2150
2151                         for (Long id : fileIds) {
2152                                 MapMessage map = session.createMapMessage();
2153                                 map.setObject("id", id);
2154                                 map.setBoolean("delete", false);
2155                                 sender.send(map);
2156                         }
2157                         sendOptimize(httpClient, 0);
2158                 } catch (DOMException e) {
2159                         throw new EJBException(e);
2160                 } catch (TransformerConfigurationException e) {
2161                         throw new EJBException(e);
2162                 } catch (IllegalArgumentException e) {
2163                         throw new EJBException(e);
2164                 } catch (HttpException e) {
2165                         throw new EJBException(e);
2166                 } catch (UnsupportedEncodingException e) {
2167                         throw new EJBException(e);
2168                 } catch (ParserConfigurationException e) {
2169                         throw new EJBException(e);
2170                 } catch (TransformerException e) {
2171                         throw new EJBException(e);
2172                 } catch (IOException e) {
2173                         throw new EJBException(e);
2174                 } catch (NamingException e) {
2175                         throw new EJBException(e);
2176                 } catch (JMSException e) {
2177                         throw new EJBException(e);
2178                 }
2179                 finally {
2180                         try {
2181                                 if (sender != null)
2182                                         sender.close();
2183                                 if (session != null)
2184                                         session.close();
2185                                 if (qConn != null)
2186                                         qConn.close();
2187                         }
2188                         catch (JMSException e) {
2189                                 logger.warn(e);
2190                         }
2191                 }
2192         }
2193
2194         /**
2195          * Sends a optimize message to the solr server
2196          *
2197          * @param httpClient
2198          * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive
2199          *                                      calls to stop the recursion
2200          * @throws UnsupportedEncodingException
2201          * @throws IOException
2202          * @throws HttpException
2203          */
2204         private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException {
2205                 PostMethod method = null;
2206                 try {
2207                         logger.debug("Optimize retry: " + retryCount);
2208                         method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2209                         method.setRequestEntity(new StringRequestEntity("<optimize/>", "text/xml", "iso8859-1"));
2210                         int statusCode = httpClient.executeMethod(method);
2211                         logger.debug("HTTP status: " + statusCode);
2212                         String response = method.getResponseBodyAsString();
2213                         logger.debug(response);
2214                         if (statusCode != 200 && retryCount < 2) {
2215                                 try {
2216                                         Thread.sleep(10000); //Give Solr a little time to be available
2217                                 } catch (InterruptedException e) {
2218                                 }
2219                                 sendOptimize(httpClient, retryCount + 1);
2220                         }
2221                 }
2222                 finally {
2223                         if (method != null)
2224                                 method.releaseConnection();
2225                 }
2226         }
2227
2228         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2229                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2230                         InsufficientPermissionsException, QuotaExceededException {
2231                 // Validate.
2232                 if (userId == null)
2233                         throw new ObjectNotFoundException("No user specified");
2234                 if (folderId == null)
2235                         throw new ObjectNotFoundException("No folder specified");
2236                 String contentType = mimeType;
2237                 if (StringUtils.isEmpty(mimeType))
2238                         contentType = DEFAULT_MIME_TYPE;
2239                 if (StringUtils.isEmpty(name))
2240                         throw new ObjectNotFoundException("No file name specified");
2241                 if (dao.existsFolderOrFile(folderId, name))
2242                         throw new DuplicateNameException("A folder or file with the name '" + name +
2243                                                 "' already exists at this level");
2244
2245                 // Do the actual work.
2246                 Folder parent = null;
2247                 try {
2248                         parent = dao.getEntityById(Folder.class, folderId);
2249                 } catch (final ObjectNotFoundException onfe) {
2250                         // Supply a more accurate problem description.
2251                         throw new ObjectNotFoundException("Parent folder not found");
2252                 }
2253                 final User owner = dao.getEntityById(User.class, userId);
2254                 if (!parent.hasWritePermission(owner))
2255                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2256                 final FileHeader file = new FileHeader();
2257                 file.setName(name);
2258                 parent.addFile(file);
2259                 // set file owner to folder owner
2260                 file.setOwner(parent.getOwner());
2261
2262                 final Date now = new Date();
2263                 final AuditInfo auditInfo = new AuditInfo();
2264                 auditInfo.setCreatedBy(owner);
2265                 auditInfo.setCreationDate(now);
2266                 auditInfo.setModifiedBy(owner);
2267                 auditInfo.setModificationDate(now);
2268                 file.setAuditInfo(auditInfo);
2269                 // TODO set the proper versioning flag on creation
2270                 file.setVersioned(false);
2271
2272                 for (final Permission p : parent.getPermissions()) {
2273                         final Permission permission = new Permission();
2274                         permission.setGroup(p.getGroup());
2275                         permission.setUser(p.getUser());
2276                         permission.setRead(p.getRead());
2277                         permission.setWrite(p.getWrite());
2278                         permission.setModifyACL(p.getModifyACL());
2279                         file.addPermission(permission);
2280                 }
2281
2282                 // Create the file body.
2283                 try {
2284                         createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2285                 } catch (FileNotFoundException e) {
2286                         throw new GSSIOException(e);
2287                 }
2288                 touchParentFolders(parent, owner, new Date());
2289                 dao.flush();
2290                 indexFile(file.getId(), false);
2291
2292                 return file.getDTO();
2293         }
2294
2295         /* (non-Javadoc)
2296          * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFileContents(java.lang.Long, java.lang.Long, java.lang.String, java.io.InputStream)
2297          */
2298         public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2299                 if (userId == null)
2300                         throw new ObjectNotFoundException("No user specified");
2301                 if (fileId == null)
2302                         throw new ObjectNotFoundException("No file specified");
2303                 String contentType = mimeType;
2304
2305                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2306
2307                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2308                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2309                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2310                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2311                         contentType = identifyMimeType(file.getName());
2312
2313                 final User owner = dao.getEntityById(User.class, userId);
2314                 if (!file.hasWritePermission(owner))
2315                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2316                 final Date now = new Date();
2317                 final AuditInfo auditInfo = new AuditInfo();
2318                 auditInfo.setCreatedBy(owner);
2319                 auditInfo.setCreationDate(now);
2320                 auditInfo.setModifiedBy(owner);
2321                 auditInfo.setModificationDate(now);
2322                 try {
2323                         createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2324                 } catch (FileNotFoundException e) {
2325                         throw new GSSIOException(e);
2326                 }
2327                 Folder parent = file.getFolder();
2328                 touchParentFolders(parent, owner, new Date());
2329
2330                 indexFile(fileId, false);
2331                 return file.getDTO();
2332         }
2333
2334         /**
2335          * Helper method for identifying mime type by examining the filename extension
2336          *
2337          * @param filename
2338          * @return the mime type
2339          */
2340         private String identifyMimeType(String filename) {
2341                 if (filename.indexOf('.') != -1) {
2342                         String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2343                         if (".doc".equals(extension))
2344                                 return "application/msword";
2345                         else if (".xls".equals(extension))
2346                                 return "application/vnd.ms-excel";
2347                         else if (".ppt".equals(extension))
2348                                 return "application/vnd.ms-powerpoint";
2349                         else if (".pdf".equals(extension))
2350                                 return "application/pdf";
2351                         else if (".gif".equals(extension))
2352                                 return "image/gif";
2353                         else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2354                                 return "image/jpeg";
2355                         else if (".tiff".equals(extension) || ".tif".equals(extension))
2356                                 return "image/tiff";
2357                         else if (".png".equals(extension))
2358                                 return "image/png";
2359                         else if (".bmp".equals(extension))
2360                                 return "image/bmp";
2361                 }
2362                 // when all else fails assign the default mime type
2363                 return DEFAULT_MIME_TYPE;
2364         }
2365
2366         /**
2367          * Helper method to create a new file body and attach it as the current body
2368          * of the provided file header.
2369          *
2370          * @param name the original file name
2371          * @param mimeType the content type
2372          * @param fileSize the uploaded file size
2373          * @param filePath the uploaded file full path
2374          * @param header the file header that will be associated with the new body
2375          * @param auditInfo the audit info
2376          * @param owner the owner of the file
2377          * @throws FileNotFoundException
2378          * @throws QuotaExceededException
2379          */
2380         private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2381                                 FileHeader header, AuditInfo auditInfo)
2382                         throws FileNotFoundException, QuotaExceededException {
2383
2384                 long currentTotalSize = 0;
2385                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2386                         currentTotalSize = header.getTotalSize();
2387                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2388                 if(quotaLeft < fileSize-currentTotalSize) {
2389                         // quota exceeded -> delete the file
2390                         deleteActualFile(filePath);
2391                         throw new QuotaExceededException("Not enough free space available");
2392                 }
2393
2394                 FileBody body = new FileBody();
2395
2396                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2397                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2398                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2399                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2400                         body.setMimeType(identifyMimeType(name));
2401                 else
2402                         body.setMimeType(mimeType);
2403                 body.setAuditInfo(auditInfo);
2404                 body.setFileSize(fileSize);
2405                 body.setOriginalFilename(name);
2406                 body.setStoredFilePath(filePath);
2407                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2408                 if(!header.isVersioned() && header.getCurrentBody() != null){
2409                         header.setCurrentBody(null);
2410                         if (header.getBodies() != null) {
2411                                 Iterator<FileBody> it = header.getBodies().iterator();
2412                                 while(it.hasNext()){
2413                                         FileBody bo = it.next();
2414                                         deleteActualFile(bo.getStoredFilePath());
2415                                         it.remove();
2416                                         dao.delete(bo);
2417                                 }
2418                         }
2419                 }
2420
2421                 dao.flush();
2422                 header.addBody(body);
2423                 header.setAuditInfo(auditInfo);
2424
2425                 dao.create(body);
2426         }
2427
2428
2429         @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2430         public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2431                 if (userId == null)
2432                         throw new ObjectNotFoundException("No user specified");
2433                 User owner = dao.getEntityById(User.class, userId);
2434                 if(owner == null)
2435                         throw new ObjectNotFoundException("No user specified");
2436                 long start = 0, end = 0;
2437                 if (logger.isDebugEnabled())
2438                         start = System.currentTimeMillis();
2439                 File result = new File(generateRepositoryFilePath());
2440                 try {
2441                         final FileOutputStream output = new FileOutputStream(result);
2442                         final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2443                         int n = 0;
2444
2445                         while (-1 != (n = stream.read(buffer)))
2446                                 output.write(buffer, 0, n);
2447                         output.close();
2448                         stream.close();
2449                 } catch (IOException e) {
2450                         if (!result.delete())
2451                                 logger.warn("Could not delete " + result.getPath());
2452                         throw e;
2453                 }
2454                 if (logger.isDebugEnabled()) {
2455                         end = System.currentTimeMillis();
2456                         logger.debug("Time to upload: " + (end - start) + " (msec)");
2457                 }
2458                 return result;
2459         }
2460
2461
2462         public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2463
2464                 if (userId == null)
2465                         throw new ObjectNotFoundException("No user specified");
2466                 User user = dao.getEntityById(User.class, userId);
2467                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2468                 if(status == null){
2469                         status = new FileUploadStatus();
2470                         status.setOwner(user);
2471                         status.setFilename(filename);
2472                         status.setBytesUploaded(bytesTransfered);
2473                         status.setFileSize(fileSize);
2474                         dao.create(status);
2475                 }
2476                 else{
2477                         status.setBytesUploaded(bytesTransfered);
2478                         status.setFileSize(fileSize);
2479                         dao.update(status);
2480                 }
2481
2482         }
2483
2484         public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2485                 if (userId == null)
2486                         throw new ObjectNotFoundException("No user specified");
2487                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2488                 if(status != null)
2489                         dao.delete(status);
2490         }
2491
2492         @Override
2493         public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2494                 return dao.getFileUploadStatus(userId, fileName);
2495         }
2496
2497         @Override
2498         public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2499                 if (userId == null)
2500                         throw new ObjectNotFoundException("No user specified");
2501                 if (folderId == null)
2502                         throw new ObjectNotFoundException("No folder specified");
2503                 final User user = dao.getEntityById(User.class, userId);
2504                 final Folder folder = dao.getEntityById(Folder.class, folderId);
2505                 // Check permissions
2506                 if (!folder.hasReadPermission(user))
2507                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2508                 List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
2509                 if (folder.hasReadPermission(user))
2510                         for (Folder f : folder.getSubfolders())
2511                                 if (f.hasReadPermission(user) && !f.isDeleted())
2512                                         subfolders.add(f.getDTO());
2513                 FolderDTO result = folder.getDTO();
2514                 result.setSubfolders(subfolders);
2515                 return folder.getDTO();
2516         }
2517
2518         @Override
2519         public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2520                 if (userId == null)
2521                         throw new ObjectNotFoundException("No user specified");
2522                 if (folderId == null)
2523                         throw new ObjectNotFoundException("No folder specified");
2524                 User user = dao.getEntityById(User.class, callingUserId);
2525                 Folder folder = dao.getEntityById(Folder.class, folderId);
2526                 // Check permissions
2527                 if (!folder.hasReadPermission(user))
2528                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2529
2530                 FolderDTO result = folder.getDTO();
2531                 result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2532                 return result;
2533         }
2534
2535         @Override
2536         public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
2537                         throws ObjectNotFoundException, InsufficientPermissionsException {
2538                 if (userId == null)
2539                         throw new ObjectNotFoundException("No user specified");
2540                 if (fileId == null)
2541                         throw new ObjectNotFoundException("No file specified");
2542                 if (version < 1)
2543                         throw new ObjectNotFoundException("No valid version specified");
2544                 User user = dao.getEntityById(User.class, userId);
2545                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2546                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2547                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2548                 FileBody body = dao.getFileVersion(fileId, version);
2549                 return body.getDTO();
2550         }
2551
2552         @Override
2553         public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2554                 if (userId == null)
2555                         throw new ObjectNotFoundException("No user specified");
2556                 User user = dao.getEntityById(User.class, userId);
2557                 user.setAcceptedPolicy(isAccepted);
2558                 return user;
2559         }
2560
2561         @Override
2562         public void updateAccounting(User user, Date date, long bandwidthDiff) {
2563                 dao.updateAccounting(user, date, bandwidthDiff);
2564         }
2565
2566         @Override
2567         public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2568                 if (userId == null)
2569                         throw new ObjectNotFoundException("No user specified");
2570                 if (folderId == null)
2571                         throw new ObjectNotFoundException("No folder specified");
2572                 User user = dao.getEntityById(User.class, userId);
2573                 Folder folder = dao.getEntityById(Folder.class, folderId);
2574                 // Check permissions
2575                 if (!folder.hasReadPermission(user))
2576                         return false;
2577                 return true;
2578         }
2579
2580         /**
2581          * Reset WebDAV password for given user.
2582          *
2583          * @param userId
2584          * @return the new password
2585          * @throws ObjectNotFoundException
2586          */
2587         @Override
2588         public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2589                 if (userId == null)
2590                         throw new ObjectNotFoundException("No user specified");
2591                 User user = dao.getEntityById(User.class, userId);
2592                 user.generateWebDAVPassword();
2593                 return user.getWebDAVPassword();
2594         }
2595
2596 }