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