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