Another fix for Issue 17. On the creation of a new folder it's public property is...
[pithos] / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java
1 /*
2  * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server.ejb;
20
21 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22 import gr.ebs.gss.client.exceptions.DuplicateNameException;
23 import gr.ebs.gss.client.exceptions.GSSIOException;
24 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
25 import gr.ebs.gss.client.exceptions.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.getSharedFilesNotInSharedFolders(userId);
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)
709                         throw new ObjectNotFoundException("The 'public' value for the specific file hasn't been specified");
710                 if (user.equals(file.getOwner()))
711                         file.setReadForAll(readForAll);
712                 if (permissions != null && !permissions.isEmpty())
713                         setFilePermissions(file, permissions);
714
715                 /*
716                  * Force constraint violation to manifest itself here.
717                  * This should cover extreme concurrency cases that the simple check
718                  * above hasn't caught.
719                  */
720                 try {
721                         dao.flush();
722                 }
723                 catch (EJBTransactionRolledbackException e) {
724                         Throwable cause = e.getCause();
725                         if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
726                                 throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
727                         throw e;
728                 }
729
730                 touchParentFolders(parent, user, new Date());
731
732                 // Re-index the file if it was modified.
733                 if (name != null || tagSet != null)
734                         indexFile(fileId, false);
735         }
736
737         @Override
738         public InputStream getFileContents(Long userId, Long fileId)
739                         throws ObjectNotFoundException, InsufficientPermissionsException {
740                 if (userId == null)
741                         throw new ObjectNotFoundException("No user specified");
742                 if (fileId == null)
743                         throw new ObjectNotFoundException("No file specified");
744
745                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
746                 User user = dao.getEntityById(User.class, userId);
747                 if (!header.hasReadPermission(user)) {
748                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
749                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
750                 }
751
752                 File f = new File(header.getCurrentBody().getStoredFilePath());
753                 try {
754                         return new FileInputStream(f);
755                 } catch (FileNotFoundException e) {
756                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
757                         throw new ObjectNotFoundException("The file contents could not be located");
758                 }
759         }
760
761         /* (non-Javadoc)
762          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long)
763          */
764         @Override
765         public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
766                 if (userId == null)
767                         throw new ObjectNotFoundException("No user specified");
768                 if (fileId == null)
769                         throw new ObjectNotFoundException("No file specified");
770                 if (bodyId == null)
771                         throw new ObjectNotFoundException("No file specified");
772
773                 final FileHeader header = dao.getEntityById(FileHeader.class, fileId);
774                 final FileBody body = dao.getEntityById(FileBody.class, bodyId);
775                 final User user = dao.getEntityById(User.class, userId);
776                 if (!header.hasReadPermission(user)) {
777                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
778                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
779                 }
780
781                 File f = new File(body.getStoredFilePath());
782                 try {
783                         return new FileInputStream(f);
784                 } catch (FileNotFoundException e) {
785                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
786                         throw new ObjectNotFoundException("The file contents could not be located");
787                 }
788         }
789
790         @Override
791         public FileHeaderDTO getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
792                 if (userId == null)
793                         throw new ObjectNotFoundException("No user specified");
794                 if (fileId == null)
795                         throw new ObjectNotFoundException("No file specified");
796                 final User user = dao.getEntityById(User.class, userId);
797                 final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
798                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
799                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
800                 return file.getDTO();
801         }
802
803         @Override
804         public FileBodyDTO getFileBody(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
805                 if (userId == null)
806                         throw new ObjectNotFoundException("No user specified");
807                 if (fileId == null)
808                         throw new ObjectNotFoundException("No file specified");
809                 User user = dao.getEntityById(User.class, userId);
810                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
811                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
812                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
813                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
814                 return body.getDTO();
815         }
816
817         @Override
818         public Object getResourceAtPath(Long ownerId, String path, boolean ignoreDeleted)
819                         throws ObjectNotFoundException {
820                 if (ownerId == null)
821                         throw new ObjectNotFoundException("No user specified");
822                 if (StringUtils.isEmpty(path))
823                         throw new ObjectNotFoundException("No path specified");
824
825                 User owner = dao.getEntityById(User.class, ownerId);
826                 List<String> pathElements = new ArrayList<String>();
827                 StringTokenizer st = new StringTokenizer(path, "/");
828                 while (st.hasMoreTokens())
829                         pathElements.add(st.nextToken());
830                 if (pathElements.size() < 1)
831                         return getRootFolder(owner.getId());
832                 // Store the last element, since it requires special handling.
833                 String lastElement = pathElements.remove(pathElements.size() - 1);
834                 FolderDTO cursor = getRootFolder(owner.getId());
835                 // Traverse and verify the specified folder path.
836                 for (String pathElement : pathElements) {
837                         cursor = getFolder(cursor.getId(), pathElement);
838                         if (cursor.isDeleted())
839                                 throw new ObjectNotFoundException("Folder " + cursor.getPath() + " not found");
840                 }
841
842                 // Use the lastElement to retrieve the actual resource.
843                 Object resource = null;
844                 try {
845                         FileHeaderDTO file = getFile(cursor.getId(), lastElement);
846                         if (ignoreDeleted && file.isDeleted())
847                                 throw new ObjectNotFoundException("Resource not found");
848                         resource = file;
849                 } catch (ObjectNotFoundException e) {
850                         // Perhaps the requested resource is not a file, so
851                         // check for folders as well.
852                         FolderDTO folder = getFolder(cursor.getId(), lastElement);
853                         if (ignoreDeleted && folder.isDeleted())
854                                 throw new ObjectNotFoundException("Resource not found");
855                         resource = folder;
856                 }
857                 return resource;
858         }
859
860         /**
861          * Retrieve a file for the specified user that has the specified name and
862          * its parent folder has id equal to folderId.
863          *
864          * @param userId the ID of the current user
865          * @param folderId the ID of the parent folder
866          * @param name the name of the requested file
867          * @return the file found
868          * @throws ObjectNotFoundException if the specified folder or file was not
869          *             found, with the exception message mentioning the precise
870          *             problem
871          */
872         private FileHeaderDTO getFile(Long folderId, String name) throws ObjectNotFoundException {
873                 if (folderId == null)
874                         throw new ObjectNotFoundException("No parent folder specified");
875                 if (StringUtils.isEmpty(name))
876                         throw new ObjectNotFoundException("No file specified");
877
878                 FileHeader file = dao.getFile(folderId, name);
879                 return file.getDTO();
880         }
881
882         /**
883          * Retrieve a folder for the specified user that has the specified name and
884          * its parent folder has id equal to parentId.
885          *
886          * @param parentId the ID of the parent folder
887          * @param name the name of the requested folder
888          * @return the folder found
889          * @throws ObjectNotFoundException if the specified folder or parent was not
890          *             found, with the exception message mentioning the precise
891          *             problem
892          */
893         private FolderDTO getFolder(Long parentId, String name) throws ObjectNotFoundException {
894                 if (parentId == null)
895                         throw new ObjectNotFoundException("No parent folder specified");
896                 if (StringUtils.isEmpty(name))
897                         throw new ObjectNotFoundException("No folder specified");
898
899                 Folder folder = dao.getFolder(parentId, name);
900                 return folder.getDTO();
901         }
902
903         private FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, InputStream resourceInputStream) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
904                 File file = null;
905                 try {
906                         file = uploadFile(resourceInputStream, userId);
907                 } catch ( IOException ioe) {
908                         // Supply a more accurate problem description.
909                         throw new GSSIOException("Problem creating file",ioe);
910                 }
911                 return updateFileContents(userId, fileId, mimeType, file.length(), file.getAbsolutePath());
912         }
913
914         @Override
915         public void copyFile(Long userId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
916                 if (userId == null)
917                         throw new ObjectNotFoundException("No user specified");
918                 if (fileId == null)
919                         throw new ObjectNotFoundException("No file specified");
920                 if (StringUtils.isEmpty(dest))
921                         throw new ObjectNotFoundException("No destination specified");
922
923                 Object destination = getResourceAtPath(userId, getParentPath(dest), true);
924                 if (!(destination instanceof FolderDTO))
925                         throw new ObjectNotFoundException("Destination parent folder not found");
926                 FolderDTO parent = (FolderDTO) destination;
927                 copyFile(userId, fileId, parent.getId(), getLastElement(dest));
928         }
929
930         @Override
931         public void copyFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
932                 if (userId == null)
933                         throw new ObjectNotFoundException("No user specified");
934                 if (ownerId == null)
935                         throw new ObjectNotFoundException("No owner specified");
936                 if (fileId == null)
937                         throw new ObjectNotFoundException("No file specified");
938                 if (StringUtils.isEmpty(dest))
939                         throw new ObjectNotFoundException("No destination specified");
940
941                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
942                 if (!(destination instanceof FolderDTO))
943                         throw new ObjectNotFoundException("Destination parent folder not found");
944                 FolderDTO parent = (FolderDTO) destination;
945                 copyFile(userId, fileId, parent.getId(), getLastElement(dest));
946         }
947
948         @Override
949         public void copyFile(Long userId, Long fileId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
950                 if (userId == null)
951                         throw new ObjectNotFoundException("No user specified");
952                 if (fileId == null)
953                         throw new ObjectNotFoundException("No file specified");
954                 if (destId == null)
955                         throw new ObjectNotFoundException("No destination specified");
956                 if (StringUtils.isEmpty(destName))
957                         throw new ObjectNotFoundException("No destination file name specified");
958
959                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
960                 Folder destination = dao.getEntityById(Folder.class, destId);
961                 User user = dao.getEntityById(User.class, userId);
962                 if (!file.hasReadPermission(user) || !destination.hasWritePermission(user))
963                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
964                 boolean versioned = file.isVersioned();
965                 int versionsNumber = file.getBodies().size();
966                 FileBody oldestBody = file.getBodies().get(0);
967                 assert oldestBody != null;
968                 File contents = new File(oldestBody.getStoredFilePath());
969                 try {
970                         createFile(user.getId(), destination.getId(), destName, oldestBody.getMimeType(), new FileInputStream(contents));
971                         FileHeader copiedFile = dao.getFile(destination.getId(), destName);
972                         copiedFile.setVersioned(versioned);
973                         dao.flush();
974                         if (versionsNumber > 1)
975                                 for (int i = 1; i < versionsNumber; i++) {
976                                         FileBody body = file.getBodies().get(i);
977                                         assert body != null;
978                                         contents = new File(body.getStoredFilePath());
979                                         updateFileContents(user.getId(), copiedFile.getId(), body.getMimeType(), new FileInputStream(contents));
980                                 }
981                         List<FileTag> tags = file.getFileTags();
982                         for (FileTag tag : tags)
983                                 createTag(userId, copiedFile.getId(), tag.getTag());
984
985                 } catch (FileNotFoundException e) {
986                         throw new ObjectNotFoundException("File contents not found for file " + contents.getAbsolutePath());
987                 }
988
989         }
990
991         @Override
992         public void copyFolder(Long userId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
993                 if (userId == null)
994                         throw new ObjectNotFoundException("No user specified");
995                 if (folderId == null)
996                         throw new ObjectNotFoundException("No folder specified");
997                 if (StringUtils.isEmpty(dest))
998                         throw new ObjectNotFoundException("No destination specified");
999
1000                 Object destination = getResourceAtPath(userId, getParentPath(dest), true);
1001                 if (!(destination instanceof FolderDTO))
1002                         throw new ObjectNotFoundException("Destination folder not found");
1003                 FolderDTO parent = (FolderDTO) destination;
1004                 copyFolder(userId, folderId, parent.getId(), getLastElement(dest));
1005         }
1006
1007         @Override
1008         public void copyFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1009                 if (userId == null)
1010                         throw new ObjectNotFoundException("No user specified");
1011                 if (folderId == null)
1012                         throw new ObjectNotFoundException("No folder specified");
1013                 if (destId == null)
1014                         throw new ObjectNotFoundException("No destination specified");
1015                 if (StringUtils.isEmpty(destName))
1016                         throw new ObjectNotFoundException("No destination folder name specified");
1017                 Folder folder = dao.getEntityById(Folder.class, folderId);
1018                 Folder destination = dao.getEntityById(Folder.class, destId);
1019                 User user = dao.getEntityById(User.class, userId);
1020                 if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
1021                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1022                 createFolder(user.getId(), destination.getId(), destName);
1023         }
1024
1025         @Override
1026         public void copyFolderStructureToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1027                 if (userId == null)
1028                         throw new ObjectNotFoundException("No user specified");
1029                 if (ownerId == null)
1030                         throw new ObjectNotFoundException("No owner specified");
1031                 if (folderId == null)
1032                         throw new ObjectNotFoundException("No folder specified");
1033                 if (StringUtils.isEmpty(dest))
1034                         throw new ObjectNotFoundException("No destination specified");
1035
1036                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1037                 if (!(destination instanceof FolderDTO))
1038                         throw new ObjectNotFoundException("Destination folder not found");
1039                 FolderDTO parent = (FolderDTO) destination;
1040                 copyFolderStructure(userId, folderId, parent.getId(), getLastElement(dest));
1041         }
1042
1043         @Override
1044         public void copyFolderStructure(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1045                 if (userId == null)
1046                         throw new ObjectNotFoundException("No user specified");
1047                 if (folderId == null)
1048                         throw new ObjectNotFoundException("No folder specified");
1049                 if (destId == null)
1050                         throw new ObjectNotFoundException("No destination specified");
1051                 if (StringUtils.isEmpty(destName))
1052                         throw new ObjectNotFoundException("No destination folder name specified");
1053
1054                 Folder folder = dao.getEntityById(Folder.class, folderId);
1055                 Folder destination = dao.getEntityById(Folder.class, destId);
1056                 final User user = dao.getEntityById(User.class, userId);
1057                 // XXX: quick fix need to copy only visible items to user (Source
1058                 // for bugs)
1059                 if (!folder.getOwner().getId().equals(userId) && !folder.hasReadPermission(user))
1060                         return;
1061                 if(folder.isDeleted())//do not copy trashed folder and contents
1062                         return;
1063                 if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
1064                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1065                 createFolder(user.getId(), destination.getId(), destName);
1066                 Folder createdFolder = dao.getFolder(destination.getId(), destName);
1067                 List<FileHeader> files = folder.getFiles();
1068                 if (files != null)
1069                         for (FileHeader file : files)
1070                                 if(!file.isDeleted())
1071                                         copyFile(userId, file.getId(), createdFolder.getId(), file.getName());
1072                 List<Folder> subFolders = folder.getSubfolders();
1073                 if (subFolders != null)
1074                         for (Folder sub : subFolders)
1075                                 if(!sub.getId().equals(createdFolder.getId()))
1076                                         copyFolderStructure(userId, sub.getId(), createdFolder.getId(), sub.getName());
1077
1078         }
1079
1080         /**
1081          * For a provided path, remove the last element and return the rest, that is
1082          * the path of the parent folder.
1083          *
1084          * @param path the specified path
1085          * @return the path of the parent folder
1086          * @throws ObjectNotFoundException if the provided string contains no path
1087          *             delimiters
1088          */
1089         private String getParentPath(String path) throws ObjectNotFoundException {
1090                 int lastDelimiter = path.lastIndexOf('/');
1091                 if (lastDelimiter == 0)
1092                         return "/";
1093                 if (lastDelimiter == -1)
1094                         // No path found.
1095                         throw new ObjectNotFoundException("There is no parent in the path: " + path);
1096                 else if (lastDelimiter < path.length() - 1)
1097                         // Return the part before the delimiter.
1098                         return path.substring(0, lastDelimiter);
1099                 else {
1100                         // Remove the trailing delimiter and then recurse.
1101                         String strippedTrail = path.substring(0, lastDelimiter);
1102                         return getParentPath(strippedTrail);
1103                 }
1104         }
1105
1106         /**
1107          * Get the last element in a path that denotes the file or folder name.
1108          *
1109          * @param path the provided path
1110          * @return the last element in the path
1111          */
1112         private String getLastElement(String path) {
1113                 int lastDelimiter = path.lastIndexOf('/');
1114                 if (lastDelimiter == -1)
1115                         // No path found.
1116                         return path;
1117                 else if (lastDelimiter < path.length() - 1)
1118                         // Return the part after the delimiter.
1119                         return path.substring(lastDelimiter + 1);
1120                 else {
1121                         // Remove the trailing delimiter and then recurse.
1122                         String strippedTrail = path.substring(0, lastDelimiter);
1123                         return getLastElement(strippedTrail);
1124                 }
1125         }
1126
1127         @Override
1128         public void moveFileToTrash(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1129                 if (userId == null)
1130                         throw new ObjectNotFoundException("No user specified");
1131                 if (fileId == null)
1132                         throw new ObjectNotFoundException("No file specified");
1133
1134                 // Do the actual work.
1135                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1136                 Folder parent = file.getFolder();
1137                 if (parent == null)
1138                         throw new ObjectNotFoundException("The specified file has no parent folder");
1139                 User user = dao.getEntityById(User.class, userId);
1140                 if (!file.hasDeletePermission(user))
1141                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1142
1143                 file.setDeleted(true);
1144                 dao.update(file);
1145                 touchParentFolders(parent, user, new Date());
1146         }
1147
1148         @Override
1149         public void moveFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
1150                 if (userId == null)
1151                         throw new ObjectNotFoundException("No user specified");
1152                 if (ownerId == null)
1153                         throw new ObjectNotFoundException("No owner specified");
1154                 if (fileId == null)
1155                         throw new ObjectNotFoundException("No file specified");
1156                 if (StringUtils.isEmpty(dest))
1157                         throw new ObjectNotFoundException("No destination specified");
1158
1159                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1160                 if (!(destination instanceof FolderDTO))
1161                         throw new ObjectNotFoundException("Destination parent folder not found");
1162                 FolderDTO parent = (FolderDTO) destination;
1163                 moveFile(userId, fileId, parent.getId(), getLastElement(dest));
1164         }
1165
1166         @Override
1167         public void moveFile(Long userId, Long fileId, Long destId, String destName) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1168                 if (userId == null)
1169                         throw new ObjectNotFoundException("No user specified");
1170                 if (fileId == null)
1171                         throw new ObjectNotFoundException("No file specified");
1172                 if (destId == null)
1173                         throw new ObjectNotFoundException("No destination specified");
1174                 if (StringUtils.isEmpty(destName))
1175                         throw new ObjectNotFoundException("No destination file name specified");
1176
1177                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1178                 Folder source = file.getFolder();
1179                 Folder destination = dao.getEntityById(Folder.class, destId);
1180
1181                 User owner = dao.getEntityById(User.class, userId);
1182                 if (!file.hasDeletePermission(owner) || !destination.hasWritePermission(owner))
1183                         throw new InsufficientPermissionsException("User " + owner.getId() + " cannot move file " + file.getName() + "(" + file.getId() + ")");
1184
1185                 // if the destination folder belongs to another user:
1186                 if (!file.getOwner().equals(destination.getOwner())) {
1187                         // (a) check if the destination quota allows the move
1188                         if(getQuotaLeft(destination.getOwner().getId()) < file.getTotalSize())
1189                                 throw new QuotaExceededException("Not enough free space available");
1190                         User newOwner = destination.getOwner();
1191                         // (b) if quota OK, change the owner of the file
1192                         file.setOwner(newOwner);
1193                         // if the file has no permission for the new owner, add it
1194                         Permission ownerPermission = null;
1195                         for (final Permission p : file.getPermissions())
1196                                 if (p.getUser() != null)
1197                                         if (p.getUser().equals(newOwner)) {
1198                                                 ownerPermission = p;
1199                                                 break;
1200                                         }
1201                         if (ownerPermission == null) {
1202                                 ownerPermission = new Permission();
1203                                 ownerPermission.setUser(newOwner);
1204                                 file.addPermission(ownerPermission);
1205                         }
1206                         ownerPermission.setRead(true);
1207                         ownerPermission.setWrite(true);
1208                         ownerPermission.setModifyACL(true);
1209                 }
1210                 // move the file to the destination folder
1211                 file.setFolder(destination);
1212                 touchParentFolders(source, owner, new Date());
1213                 touchParentFolders(destination, owner, new Date());
1214         }
1215
1216         @Override
1217         public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
1218                 if (userId == null)
1219                         throw new ObjectNotFoundException("No user specified");
1220                 if (ownerId == null)
1221                         throw new ObjectNotFoundException("No owner specified");
1222                 if (folderId == null)
1223                         throw new ObjectNotFoundException("No folder specified");
1224                 if (StringUtils.isEmpty(dest))
1225                         throw new ObjectNotFoundException("No destination specified");
1226
1227                 Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1228                 if (!(destination instanceof FolderDTO))
1229                         throw new ObjectNotFoundException("Destination parent folder not found");
1230                 FolderDTO parent = (FolderDTO) destination;
1231                 moveFolder(userId, folderId, parent.getId(), getLastElement(dest));
1232         }
1233
1234         @Override
1235         public void moveFolder(Long userId, Long folderId, Long destId, String destName)
1236                         throws ObjectNotFoundException, InsufficientPermissionsException,
1237                         QuotaExceededException {
1238                 Folder source = dao.getEntityById(Folder.class, folderId);
1239                 Folder destination = dao.getEntityById(Folder.class, destId);
1240                 User user = dao.getEntityById(User.class, userId);
1241                 User sourceOwner = source.getOwner();
1242                 User destinationOwner = destination.getOwner();
1243                 // Do not move trashed folders and contents.
1244                 if (source.isDeleted())
1245                         return;
1246                 // Check permissions.
1247                 if (!destination.hasWritePermission(user)
1248                                 || !source.hasReadPermission(user)
1249                                 || !source.hasWritePermission(user))
1250                         throw new InsufficientPermissionsException("You don't have the " +
1251                                         "necessary permissions");
1252                 // Use the same timestamp for all subsequent modifications to make
1253                 // changes appear simultaneous.
1254                 Date now = new Date();
1255                 // If source and destination are not in the same user's namespace,
1256                 // change owners and check quota.
1257                 if (!sourceOwner.equals(destinationOwner)) {
1258                         changeOwner(source, destinationOwner, user, now);
1259                         if (getQuotaLeft(destinationOwner.getId()) < 0)
1260                                 throw new QuotaExceededException("Not enough free space " +
1261                                                 "available in destination folder");
1262                 }
1263                 // Perform the move.
1264                 Folder oldParent = source.getParent();
1265                 oldParent.removeSubfolder(source);
1266                 destination.addSubfolder(source);
1267                 // Mark the former parent and destination trees upwards as modified.
1268                 touchParentFolders(oldParent, user, now);
1269                 touchParentFolders(source, user, now);
1270         }
1271
1272         /**
1273          * Recursively change the owner of the specified folder and all of its
1274          * contents to the specified owner. Also mark them all as modified with the
1275          * specified modifier and modificationDate.
1276          */
1277         private void changeOwner(Folder folder, User owner, User modifier, Date modificationDate) {
1278                 for (FileHeader file: folder.getFiles()) {
1279                         file.setOwner(owner);
1280                         file.getAuditInfo().setModificationDate(modificationDate);
1281                         file.getAuditInfo().setModifiedBy(modifier);
1282                 }
1283                 for (Folder sub: folder.getSubfolders())
1284                         changeOwner(sub, owner, modifier, modificationDate);
1285                 folder.setOwner(owner);
1286                 folder.getAuditInfo().setModificationDate(modificationDate);
1287                 folder.getAuditInfo().setModifiedBy(modifier);
1288         }
1289
1290         @Override
1291         public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
1292                 // Validate.
1293                 if (userId == null)
1294                         throw new ObjectNotFoundException("No user specified");
1295
1296                 // Do the actual work.
1297                 final List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1298                 final List<FileHeader> files = dao.getDeletedFiles(userId);
1299                 for (final FileHeader f : files)
1300                         result.add(f.getDTO());
1301                 return result;
1302         }
1303
1304         @Override
1305         public void removeFileFromTrash(Long userId, Long fileId)
1306                         throws ObjectNotFoundException, InsufficientPermissionsException {
1307                 if (userId == null)
1308                         throw new ObjectNotFoundException("No user specified");
1309                 if (fileId == null)
1310                         throw new ObjectNotFoundException("No file specified");
1311
1312                 // Do the actual work.
1313                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1314                 Folder parent = file.getFolder();
1315                 if (parent == null)
1316                         throw new ObjectNotFoundException("The specified file has no parent folder");
1317                 User user = dao.getEntityById(User.class, userId);
1318                 if (!file.hasDeletePermission(user))
1319                         throw new InsufficientPermissionsException("User " + user.getUsername() +
1320                                                 " cannot restore file " + file.getName());
1321
1322                 file.setDeleted(false);
1323                 dao.update(file);
1324                 touchParentFolders(parent, user, new Date());
1325         }
1326
1327         @Override
1328         public void moveFolderToTrash(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1329                 if (userId == null)
1330                         throw new ObjectNotFoundException("No user specified");
1331                 if (folderId == null)
1332                         throw new ObjectNotFoundException("No folder specified");
1333                 Folder folder = dao.getEntityById(Folder.class, folderId);
1334                 User user = dao.getEntityById(User.class, userId);
1335                 if (!folder.hasDeletePermission(user))
1336                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1337                 folder.setDeleted(true);
1338                 dao.update(folder);
1339                 touchParentFolders(folder, user, new Date());
1340                 for (FileHeader file : folder.getFiles())
1341                         moveFileToTrash(userId, file.getId());
1342                 for (Folder subFolder : folder.getSubfolders())
1343                         moveFolderToTrash(userId, subFolder.getId());
1344
1345         }
1346
1347         @Override
1348         public void removeFolderFromTrash(Long userId, Long folderId)
1349                         throws ObjectNotFoundException, InsufficientPermissionsException {
1350                 if (userId == null)
1351                         throw new ObjectNotFoundException("No user specified");
1352                 if (folderId == null)
1353                         throw new ObjectNotFoundException("No folder specified");
1354                 Folder folder = dao.getEntityById(Folder.class, folderId);
1355                 User user = dao.getEntityById(User.class, userId);
1356                 if (!folder.hasDeletePermission(user))
1357                         throw new InsufficientPermissionsException("User " + user.getUsername() +
1358                                                 " cannot restore folder " + folder.getName());
1359                 folder.setDeleted(false);
1360                 for (FileHeader file : folder.getFiles())
1361                         removeFileFromTrash(userId, file.getId());
1362                 for (Folder subFolder : folder.getSubfolders())
1363                         removeFolderFromTrash(userId, subFolder.getId());
1364                 dao.update(folder);
1365                 touchParentFolders(folder, user, new Date());
1366         }
1367
1368         @Override
1369         public List<FolderDTO> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
1370                 List<Folder> folders = dao.getDeletedRootFolders(userId);
1371                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1372                 for (Folder folder : folders)
1373                         result.add(folder.getDTO());
1374                 return result;
1375         }
1376
1377         @Override
1378         public void emptyTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1379                 List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1380                 for (FolderDTO fdto : deletedRootFolders)
1381                         deleteFolder(userId, fdto.getId());
1382                 List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1383                 for (FileHeaderDTO filedto : deletedFiles)
1384                         deleteFile(userId, filedto.getId());
1385         }
1386
1387         @Override
1388         public void restoreTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1389                 List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1390                 for (FolderDTO fdto : deletedRootFolders)
1391                         removeFolderFromTrash(userId, fdto.getId());
1392                 List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1393                 for (FileHeaderDTO filedto : deletedFiles)
1394                         removeFileFromTrash(userId, filedto.getId());
1395         }
1396
1397         @Override
1398         public User createUser(String username, String name, String mail,
1399                                 String idp, String idpid) throws ObjectNotFoundException {
1400                 if (username == null)
1401                         throw new ObjectNotFoundException("No username specified");
1402                 if (name == null)
1403                         throw new ObjectNotFoundException("No name specified");
1404
1405                 User user = new User();
1406                 user.setUsername(username);
1407                 user.setName(name);
1408                 user.setEmail(mail);
1409                 user.setIdentityProvider(idp);
1410                 user.setIdentityProviderId(idpid);
1411                 Date now = new Date();
1412                 AuditInfo auditInfo = new AuditInfo();
1413                 auditInfo.setCreationDate(now);
1414                 auditInfo.setModificationDate(now);
1415                 user.setAuditInfo(auditInfo);
1416                 user.setActive(true);
1417                 user.generateAuthToken();
1418                 user.generateWebDAVPassword();
1419                 user.setUserClass(getDefaultUserClass());
1420                 dao.create(user);
1421                 // Make sure we get an ID in the user object.
1422                 dao.flush();
1423                 // Create the root folder for the user.
1424                 createFolder(user.getName(), null, user);
1425                 return user;
1426         }
1427
1428         /**
1429          * Get the default user class, which is the one with the lowest quota.
1430          */
1431         private UserClass getDefaultUserClass() {
1432                 return getUserClasses().get(0);
1433         }
1434
1435         @Override
1436         public List<UserClass> getUserClasses() {
1437                 List<UserClass> classes = dao.getUserClasses();
1438                 // Create a default user class for first-time use. Afterwards, the
1439                 // admin should modify or add to the userclass table.
1440                 if (classes.size() == 0) {
1441                         UserClass defaultClass = new UserClass();
1442                         defaultClass.setName("default");
1443                         Long defaultQuota = getConfiguration().getLong("quota", new Long(52428800L));
1444                         defaultClass.setQuota(defaultQuota);
1445                         dao.create(defaultClass);
1446                         classes.add(defaultClass);
1447                 }
1448                 return classes;
1449         }
1450
1451         @Override
1452         public User findUserByEmail(String email) {
1453                 return dao.findUserByEmail(email);
1454         }
1455
1456         @Override
1457         public void updateUser(User user) {
1458                 dao.update(user);
1459         }
1460
1461         @Override
1462         public User findUser(String username) {
1463                 if (username == null)
1464                         return null;
1465                 return dao.findUser(username);
1466         }
1467
1468         @Override
1469         public User updateUserToken(Long userId) throws ObjectNotFoundException {
1470                 if (userId == null)
1471                         throw new ObjectNotFoundException("No user specified");
1472                 User user = dao.getEntityById(User.class, userId);
1473                 user.generateAuthToken();
1474                 return user;
1475         }
1476
1477         @Override
1478         public Set<PermissionDTO> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1479                 if (userId == null)
1480                         throw new ObjectNotFoundException("No user specified");
1481                 if (folderId == null)
1482                         throw new ObjectNotFoundException("No folder specified");
1483                 User user = dao.getEntityById(User.class, userId);
1484                 Folder folder = dao.getEntityById(Folder.class, folderId);
1485                 if(!folder.hasReadPermission(user))
1486                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1487                 Set<Permission> perms = folder.getPermissions();
1488                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1489                 for (Permission perm : perms)
1490                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1491                                 result.add(perm.getDTO());
1492                 for (Permission perm : perms)
1493                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1494                         } else
1495                                 result.add(perm.getDTO());
1496                 return result;
1497
1498         }
1499
1500         /**
1501          * Set the provided permissions as the new permissions of the specified
1502          * folder.
1503          *
1504          * @param user
1505          * @param folder
1506          * @param permissions
1507          * @throws ObjectNotFoundException
1508          * @throws InsufficientPermissionsException
1509          */
1510         private void setFolderPermissions(User user, Folder folder, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1511                 if (permissions != null && !permissions.isEmpty()) {
1512                         User owner = folder.getOwner();
1513                         PermissionDTO ownerPerm = null;
1514                         for (PermissionDTO dto : permissions)
1515                                 if (dto.getUser() != null && dto.getUser().getId().equals(owner.getId())) {
1516                                         ownerPerm = dto;
1517                                         break;
1518                                 }
1519                         if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1520                                 throw new InsufficientPermissionsException("Can't remove permissions from owner");
1521                         // Delete previous entries
1522                         for (Permission perm: folder.getPermissions())
1523                                 dao.delete(perm);
1524                         folder.getPermissions().clear();
1525                         for (PermissionDTO dto : permissions) {
1526                                 // Skip 'empty' permission entries.
1527                                 if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1528                                 folder.addPermission(getPermission(dto));
1529                         }
1530                         dao.update(folder);
1531                         for (FileHeader file : folder.getFiles()) {
1532                                 setFilePermissions(file, permissions);
1533                                 Date now = new Date();
1534                                 file.getAuditInfo().setModificationDate(now);
1535                                 file.getAuditInfo().setModifiedBy(user);
1536                         }
1537                         for (Folder sub : folder.getSubfolders())
1538                                 setFolderPermissions(user, sub, permissions);
1539                 }
1540         }
1541
1542         private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
1543                 Permission res = new Permission();
1544                 if (dto.getGroup() != null)
1545                         res.setGroup(dao.getEntityById(Group.class, dto.getGroup().getId()));
1546                 else if (dto.getUser() != null)
1547                         if (dto.getUser().getId() == null)
1548                                 res.setUser(dao.getUser(dto.getUser().getUsername()));
1549                         else
1550                                 res.setUser(dao.getEntityById(User.class, dto.getUser().getId()));
1551                 res.setRead(dto.hasRead());
1552                 res.setWrite(dto.hasWrite());
1553                 res.setModifyACL(dto.hasModifyACL());
1554                 return res;
1555         }
1556
1557         /* (non-Javadoc)
1558          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
1559          */
1560         @Override
1561         public List<UserDTO> getUsersByUserNameLike(String username) {
1562                 List<User> users = dao.getUsersByUserNameLike(username);
1563                 List<UserDTO> result = new ArrayList<UserDTO>();
1564                 for (User u : users)
1565                         result.add(u.getDTO());
1566                 return result;
1567
1568         }
1569
1570         @Override
1571         public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1572                 if (userId == null)
1573                         throw new ObjectNotFoundException("No user specified");
1574                 if (groupId == null)
1575                         throw new ObjectNotFoundException("No group specified");
1576                 if (userToAddId == null)
1577                         throw new ObjectNotFoundException("No user to add specified");
1578                 User user = dao.getEntityById(User.class, userId);
1579                 Group group = dao.getEntityById(Group.class, groupId);
1580                 if (!group.getOwner().equals(user))
1581                         throw new InsufficientPermissionsException();
1582                 User userToAdd = dao.getEntityById(User.class, userToAddId);
1583                 if (group.contains(userToAdd))
1584                         throw new DuplicateNameException("User already exists in group");
1585                 group.getMembers().add(userToAdd);
1586                 dao.update(group);
1587
1588         }
1589
1590         @Override
1591         public void invalidateUserToken(Long userId) throws ObjectNotFoundException {
1592                 if (userId == null)
1593                         throw new ObjectNotFoundException("No user specified");
1594                 User user = dao.getEntityById(User.class, userId);
1595                 user.invalidateAuthToken();
1596                 return;
1597         }
1598
1599         @Override
1600         public List<FolderDTO> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
1601                 if (userId == null)
1602                         throw new ObjectNotFoundException("No user specified");
1603                 List<Folder> folders = dao.getSharedRootFolders(userId);
1604                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1605                 for (Folder f : folders) {
1606                         FolderDTO dto = f.getDTO();
1607                         dto.setSubfolders(getSharedSubfolders(userId, f.getId()));
1608                         result.add(dto);
1609                 }
1610                 return result;
1611         }
1612
1613         @Override
1614         public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
1615                 if (userId == null)
1616                         throw new ObjectNotFoundException("No user specified");
1617                 if (groupId == null)
1618                         throw new ObjectNotFoundException("No group specified");
1619                 if (memberId == null)
1620                         throw new ObjectNotFoundException("No member specified");
1621                 User owner = dao.getEntityById(User.class, userId);
1622                 Group group = dao.getEntityById(Group.class, groupId);
1623                 User member = dao.getEntityById(User.class, memberId);
1624                 if (!group.getOwner().equals(owner))
1625                         throw new InsufficientPermissionsException("User is not the owner of the group");
1626                 group.removeMemberFromGroup(member);
1627                 dao.update(group);
1628
1629         }
1630
1631         @Override
1632         public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
1633                 List<User> users = dao.getUsersSharingFoldersForUser(userId);
1634                 List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
1635                 List<UserDTO> res = new ArrayList<UserDTO>();
1636                 for (User u : users)
1637                         res.add(u.getDTO());
1638                 for(User fu : usersFiles)
1639                         if(!users.contains(fu))
1640                                 res.add(fu.getDTO());
1641                 return res;
1642         }
1643
1644         @Override
1645         public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1646                 if (userId == null)
1647                         throw new ObjectNotFoundException("No user specified");
1648                 if (fileId == null)
1649                         throw new ObjectNotFoundException("No folder specified");
1650                 User user = dao.getEntityById(User.class, userId);
1651                 FileHeader folder = dao.getEntityById(FileHeader.class, fileId);
1652                 if(!folder.hasReadPermission(user))
1653                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1654                 Set<Permission> perms = folder.getPermissions();
1655                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1656                 for (Permission perm : perms)
1657                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1658                                 result.add(perm.getDTO());
1659                 for (Permission perm : perms)
1660                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1661                         } else
1662                                 result.add(perm.getDTO());
1663                 return result;
1664         }
1665
1666         /**
1667          * Set the provided permissions as the new permissions of the specified
1668          * file. This method sets the modification date/user attributes to the
1669          * current values as a side effect.
1670          *
1671          * @param file
1672          * @param permissions
1673          * @throws ObjectNotFoundException
1674          * @throws InsufficientPermissionsException
1675          */
1676         private void setFilePermissions(FileHeader file,
1677                                 Set<PermissionDTO> permissions)
1678                         throws ObjectNotFoundException, InsufficientPermissionsException {
1679                 if (permissions != null && !permissions.isEmpty()) {
1680                         PermissionDTO ownerPerm = null;
1681                         for (PermissionDTO dto : permissions)
1682                                 if (dto.getUser() != null && dto.getUser().getId().equals(file.getOwner().getId())) {
1683                                         ownerPerm = dto;
1684                                         break;
1685                                 }
1686                         if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1687                                 throw new InsufficientPermissionsException("Can't remove permissions from owner");
1688                         // Delete previous entries.
1689                         for (Permission perm: file.getPermissions())
1690                                 dao.delete(perm);
1691                         file.getPermissions().clear();
1692                         for (PermissionDTO dto : permissions) {
1693                                 // Skip 'empty' permission entries.
1694                                 if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1695                                 file.addPermission(getPermission(dto));
1696                         }
1697                         dao.flush();
1698                 }
1699         }
1700
1701         @Override
1702         public List<FileHeaderDTO> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1703                 if (userId == null)
1704                         throw new ObjectNotFoundException("No user specified");
1705                 List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1706                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1707                 for (FileHeader f : files)
1708                         result.add(f.getDTO());
1709                 return result;
1710         }
1711
1712         @Override
1713         public List<FileHeaderDTO> getSharedFiles(Long userId) throws ObjectNotFoundException {
1714                 if (userId == null)
1715                         throw new ObjectNotFoundException("No user specified");
1716                 List<FileHeader> files = dao.getSharedFiles(userId);
1717                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1718                 for (FileHeader f : files)
1719                         result.add(f.getDTO());
1720                 return result;
1721         }
1722
1723         @Override
1724         public List<FolderDTO> getSharedFolders(Long userId) throws ObjectNotFoundException {
1725                 if (userId == null)
1726                         throw new ObjectNotFoundException("No user specified");
1727                 List<Folder> folders = dao.getSharedFolders(userId);
1728                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1729                 for (Folder f : folders)
1730                         result.add(f.getDTO());
1731                 return result;
1732         }
1733
1734         @Override
1735         public List<FileHeaderDTO> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1736                 if (ownerId == null)
1737                         throw new ObjectNotFoundException("No owner specified");
1738                 if (callingUserId == null)
1739                         throw new ObjectNotFoundException("No calling user specified");
1740                 List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1741                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1742                 for (FileHeader f : folders)
1743                         result.add(f.getDTO());
1744                 return result;
1745         }
1746
1747         @Override
1748         public List<FolderDTO> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1749                 if (ownerId == null)
1750                         throw new ObjectNotFoundException("No owner specified");
1751                 if (callingUserId == null)
1752                         throw new ObjectNotFoundException("No calling user specified");
1753                 List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
1754                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1755                 for (Folder f : folders) {
1756                         FolderDTO dto = f.getDTO();
1757                         dto.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
1758                         result.add(dto);
1759                 }
1760                 return result;
1761
1762         }
1763
1764         @Override
1765         public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1766                 if (userId == null)
1767                         throw new ObjectNotFoundException("No user specified");
1768                 if (folderId == null)
1769                         throw new ObjectNotFoundException("No folder specified");
1770                 User user = dao.getEntityById(User.class, userId);
1771                 Folder folder = dao.getEntityById(Folder.class, folderId);
1772                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1773                 if (folder.isShared(user))
1774                         for (Folder f : folder.getSubfolders())
1775                                 if (f.isShared(user) && !f.isDeleted())
1776                                         result.add(f.getDTO());
1777                 return result;
1778         }
1779
1780         @Override
1781         public List<FolderDTO> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
1782                 if (userId == null)
1783                         throw new ObjectNotFoundException("No user specified");
1784                 if (callingUserId == null)
1785                         throw new ObjectNotFoundException("No user specified");
1786                 if (folderId == null)
1787                         throw new ObjectNotFoundException("No folder specified");
1788                 User user = dao.getEntityById(User.class, callingUserId);
1789                 Folder folder = dao.getEntityById(Folder.class, folderId);
1790                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1791                 if (folder.isSharedForOtherUser(user))
1792                         for (Folder f : folder.getSubfolders())
1793                                 if (f.isSharedForOtherUser(user) && !f.isDeleted()){
1794                                         FolderDTO dto = f.getDTO();
1795                                         dto.setSubfolders(getSharedSubfolders(userId, callingUserId, dto.getId()));
1796                                         result.add(dto);
1797                                 }
1798                 return result;
1799
1800         }
1801
1802         @Override
1803         public List<FileHeaderDTO> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1804                 if (userId == null)
1805                         throw new ObjectNotFoundException("No user specified");
1806                 User user = getUser(userId);
1807                 if (query == null)
1808                         throw new ObjectNotFoundException("No query specified");
1809                 List<FileHeader> files = search(user.getId(), query);
1810                 List<FileHeaderDTO> res = new ArrayList<FileHeaderDTO>();
1811                 for(FileHeader f : files)
1812                         res.add(f.getDTO());
1813                 return res;
1814         }
1815
1816         /**
1817          * Performs the actuals search on the solr server and returns the results
1818          *
1819          * We have to use the dismax query type (instead of the
1820          * standard) because it allows for search time field boosting. This is because we can't use indexing
1821          * time field boosting due to the patched rich indexing API that does not allow it
1822          *
1823          * @param userId
1824          * @param query
1825          * @return a List of FileHeader objects
1826          */
1827         private List<FileHeader> search(Long userId, String query) {
1828                 try {
1829                         HttpClient httpClient = new HttpClient();
1830
1831                         GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl"));
1832                         NameValuePair[] params = {new NameValuePair("qt", "dismax"),
1833                                                                                 new NameValuePair("q", query),
1834                                                                                 new NameValuePair("sort", "score desc"),
1835                                                                                 new NameValuePair("indent", "on")};
1836                         method.setQueryString(params);
1837                         int retryCount = 0;
1838                         int statusCode = 0;
1839                         String response = null;
1840                         do {
1841                                 statusCode = httpClient.executeMethod(method);
1842                                 logger.debug("HTTP status: " + statusCode);
1843                                 response = method.getResponseBodyAsString();
1844                                 logger.debug(response);
1845                                 retryCount++;
1846                                 if (statusCode != 200 && retryCount < 3)
1847                                         try {
1848                                                 Thread.sleep(3000); //Give Solr a little time to be available
1849                                         } catch (InterruptedException e) {
1850                                         }
1851                         } while (statusCode != 200 && retryCount < 3);
1852                         if (statusCode != 200)
1853                                 throw new EJBException("Search query return error:\n" + response);
1854
1855                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1856                         DocumentBuilder db = dbf.newDocumentBuilder();
1857                         Document doc = db.parse(method.getResponseBodyAsStream());
1858                         method.releaseConnection();
1859
1860                         Node root = doc.getElementsByTagName("response").item(0);
1861                         Node lst = root.getFirstChild().getNextSibling();
1862                         Node status = lst.getFirstChild().getNextSibling();
1863                         if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") &&
1864                                 status.getTextContent().equals("0")) {
1865                                 List<FileHeader> fileResult = new ArrayList<FileHeader>();
1866                                 Node result = lst.getNextSibling().getNextSibling();
1867                                 NodeList docs = result.getChildNodes();
1868                                 User user = getUser(userId);
1869                                 for (int i=1; i<docs.getLength(); i=i+2) {
1870                                         Node d = docs.item(i);
1871                                         NodeList docData = d.getChildNodes();
1872                                         for (int j=1; j<docData.getLength(); j=j+2) {
1873                                                 Node dd = docData.item(j);
1874                                                 if (dd.getAttributes().item(0).getNodeName().equals("name") &&
1875                                                         dd.getAttributes().item(0).getNodeValue().equals("id")) {
1876                                                         Long fileId = Long.valueOf(dd.getTextContent());
1877                                                         try {
1878                                                                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1879                                                                 if (file.hasReadPermission(user)) {
1880                                                                         fileResult.add(file);
1881                                                                         logger.debug("File added " + fileId);
1882                                                                 }
1883                                                         } catch (ObjectNotFoundException e) {
1884                                                                 logger.warn("Search result not found", e);
1885                                                         }
1886                                                 }
1887                                         }
1888                                 }
1889                                 return fileResult;
1890                         }
1891                         throw new EJBException();
1892                 } catch (HttpException e) {
1893                         throw new EJBException(e);
1894                 } catch (IOException e) {
1895                         throw new EJBException(e);
1896                 } catch (SAXException e) {
1897                         throw new EJBException(e);
1898                 } catch (ParserConfigurationException e) {
1899                         throw new EJBException(e);
1900                 } catch (ObjectNotFoundException e) {
1901                         throw new EJBException(e);
1902                 }
1903         }
1904
1905         @Override
1906         public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1907                 for(Long l : fileIds){
1908                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1909                         copyFile(userId, l, destId, file.getName());
1910                 }
1911
1912
1913         }
1914
1915         @Override
1916         public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1917                 for(Long l : fileIds){
1918                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1919                         moveFile(userId, l, destId, file.getName());
1920                 }
1921
1922         }
1923
1924         @Override
1925         public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1926                 if (userId == null)
1927                         throw new ObjectNotFoundException("No user specified");
1928                 final User user = dao.getEntityById(User.class, userId);
1929                 List<String> filesToRemove = new ArrayList<String>();
1930                 //first delete database objects
1931                 for(Long fileId : fileIds){
1932                         if (fileId == null)
1933                                 throw new ObjectNotFoundException("No file specified");
1934                         final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1935                         final Folder parent = file.getFolder();
1936                         if (parent == null)
1937                                 throw new ObjectNotFoundException("The specified file has no parent folder");
1938                         if (!file.hasDeletePermission(user))
1939                                 throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1940
1941                         parent.removeFile(file);
1942                         for (final FileBody body : file.getBodies())
1943                                 filesToRemove.add(body.getStoredFilePath());
1944                         dao.delete(file);
1945                         touchParentFolders(parent, user, new Date());
1946                 }
1947                 //then remove physical files if everything is ok
1948                 for(String physicalFileName : filesToRemove)
1949                         deleteActualFile(physicalFileName);
1950                 //then unindex deleted files
1951                 for(Long fileId : fileIds)
1952                         indexFile(fileId, true);
1953
1954         }
1955
1956         @Override
1957         public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1958                 for(Long l : fileIds)
1959                         moveFileToTrash(userId, l);
1960
1961         }
1962
1963         @Override
1964         public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1965                 for(Long l : fileIds)
1966                         removeFileFromTrash(userId, l);
1967
1968         }
1969
1970         @Override
1971         public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1972                 if (userId == null)
1973                         throw new ObjectNotFoundException("No user specified");
1974                 User user = dao.getEntityById(User.class, userId);
1975                 Nonce nonce = Nonce.createNonce(user.getId());
1976                 dao.create(nonce);
1977                 return nonce;
1978         }
1979
1980         @Override
1981         public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1982                 if (userId == null)
1983                         throw new ObjectNotFoundException("No user specified");
1984                 if (nonce == null)
1985                         throw new ObjectNotFoundException("No nonce specified");
1986                 return dao.getNonce(nonce, userId);
1987         }
1988
1989         @Override
1990         public void removeNonce(Long id) throws ObjectNotFoundException {
1991                 if (id == null)
1992                         throw new ObjectNotFoundException("No nonce specified");
1993                 Nonce nonce = dao.getEntityById(Nonce.class, id);
1994                 dao.delete(nonce);
1995         }
1996
1997         @Override
1998         public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1999                 if (userId == null)
2000                         throw new ObjectNotFoundException("No user specified");
2001                 User user = dao.getEntityById(User.class, userId);
2002                 user.setNonce(nonce);
2003                 user.setNonceExpiryDate(nonceExpiryDate);
2004         }
2005
2006         @Override
2007         public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
2008                 if (userId == null)
2009                         throw new ObjectNotFoundException("No user specified");
2010                 StatsDTO stats = new StatsDTO();
2011                 stats.setFileCount(dao.getFileCount(userId));
2012                 Long fileSize = dao.getFileSize(userId);
2013                 stats.setFileSize(fileSize);
2014                 Long quota = getQuota(userId);
2015                 Long quotaLeft = quota - fileSize;
2016                 stats.setQuotaLeftSize(quotaLeft);
2017                 return stats;
2018         }
2019
2020         @Override
2021         public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2022                 if (userId == null)
2023                         throw new ObjectNotFoundException("No user specified");
2024                 if (fileId == null)
2025                         throw new ObjectNotFoundException("No file specified");
2026                 User user = dao.getEntityById(User.class, userId);
2027                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2028                 if(!header.hasReadPermission(user))
2029                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2030                 List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
2031                 for(int i = header.getBodies().size()-1 ; i>=0; i--)
2032                         result.add(header.getBodies().get(i).getDTO());
2033                 return result;
2034         }
2035
2036         @Override
2037         public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
2038                 if (userId == null)
2039                         throw new ObjectNotFoundException("No user specified");
2040                 if (fileId == null)
2041                         throw new ObjectNotFoundException("No file specified");
2042                 if (bodyId == null)
2043                         throw new ObjectNotFoundException("No body specified");
2044                 User user = dao.getEntityById(User.class, userId);
2045                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2046                 if(!header.hasWritePermission(user))
2047                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2048                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
2049                 if(body.equals(header.getCurrentBody())){
2050
2051                         if(header.getBodies().size() == 1)
2052                                 throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
2053                         for(FileBody b : header.getBodies())
2054                                 if(b.getVersion() == body.getVersion()-1)
2055                                         header.setCurrentBody(b);
2056                 }
2057                 deleteActualFile(body.getStoredFilePath());
2058                 header.getBodies().remove(body);
2059
2060                 Folder parent = header.getFolder();
2061                 touchParentFolders(parent, user, new Date());
2062
2063         }
2064
2065         @Override
2066         public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
2067                 if (userId == null)
2068                         throw new ObjectNotFoundException("No user specified");
2069                 if (fileId == null)
2070                         throw new ObjectNotFoundException("No file specified");
2071                 User user = dao.getEntityById(User.class, userId);
2072                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2073                 if(!header.hasWritePermission(user))
2074                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2075                 FileBody body = dao.getFileVersion(fileId, version);
2076                 final File fileContents = new File(body.getStoredFilePath());
2077
2078                 try {
2079                         updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2080                 } catch (FileNotFoundException e) {
2081                         throw new GSSIOException(e);
2082                 }
2083
2084         }
2085
2086         /* (non-Javadoc)
2087          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
2088          */
2089         @Override
2090         public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2091                 if (userId == null)
2092                         throw new ObjectNotFoundException("No user specified");
2093                 if (fileId == null)
2094                         throw new ObjectNotFoundException("No file specified");
2095                 User user = dao.getEntityById(User.class, userId);
2096                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2097                 if(!header.hasWritePermission(user))
2098                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2099                 Iterator<FileBody> it = header.getBodies().iterator();
2100                 while(it.hasNext()){
2101                         FileBody body = it.next();
2102                         if(!body.equals(header.getCurrentBody())){
2103                                 deleteActualFile(body.getStoredFilePath());
2104                                 it.remove();
2105                                 dao.delete(body);
2106                         }
2107                 }
2108                 header.getCurrentBody().setVersion(1);
2109
2110                 Folder parent = header.getFolder();
2111                 touchParentFolders(parent, user, new Date());
2112         }
2113
2114         /**
2115          * Gets the quota left for specified user ID.
2116          */
2117         private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
2118                 Long fileSize = dao.getFileSize(userId);
2119                 Long quota = getQuota(userId);
2120                 return quota - fileSize;
2121         }
2122
2123         /**
2124          * Gets the quota for specified user ID.
2125          */
2126         private Long getQuota(Long userId) throws ObjectNotFoundException{
2127                 UserClass uc = getUser(userId).getUserClass();
2128                 if (uc == null)
2129                         uc = getDefaultUserClass();
2130                 return uc.getQuota();
2131         }
2132
2133         @Override
2134         public void rebuildSolrIndex() {
2135                 MessageProducer sender = null;
2136                 Session session = null;
2137                 Connection qConn = null;
2138                 try {
2139                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2140                         DocumentBuilder db = dbf.newDocumentBuilder();
2141                         Document doc = db.newDocument();
2142                         Node root = doc.createElement("delete");
2143                         doc.appendChild(root);
2144                         Node queryNode = doc.createElement("query");
2145                         root.appendChild(queryNode);
2146                         queryNode.appendChild(doc.createTextNode("*:*"));
2147
2148                         TransformerFactory fact = TransformerFactory.newInstance();
2149                         Transformer trans = fact.newTransformer();
2150                         trans.setOutputProperty(OutputKeys.INDENT, "yes");
2151                         StringWriter sw = new StringWriter();
2152                         StreamResult sr = new StreamResult(sw);
2153                         DOMSource source = new DOMSource(doc);
2154                         trans.transform(source, sr);
2155                         logger.debug(sw.toString());
2156
2157                         HttpClient httpClient = new HttpClient();
2158                         PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2159                         method.setRequestEntity(new StringRequestEntity(sw.toString()));
2160                         int retryCount = 0;
2161                         int statusCode = 0;
2162                         String response = null;
2163                         do {
2164                                 statusCode = httpClient.executeMethod(method);
2165                                 logger.debug("HTTP status: " + statusCode);
2166                                 response = method.getResponseBodyAsString();
2167                                 logger.debug(response);
2168                                 retryCount++;
2169                                 if (statusCode != 200 && retryCount < 3)
2170                                         try {
2171                                                 Thread.sleep(10000); //Give Solr a little time to be available
2172                                         } catch (InterruptedException e) {
2173                                         }
2174                         } while (statusCode != 200 && retryCount < 3);
2175                         method.releaseConnection();
2176                         if (statusCode != 200)
2177                                 throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response);
2178                         List<Long> fileIds = dao.getAllFileIds();
2179
2180                         Context jndiCtx = new InitialContext();
2181                         ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
2182                         Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
2183                         qConn = factory.createConnection();
2184                         session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
2185                         sender = session.createProducer(queue);
2186
2187                         for (Long id : fileIds) {
2188                                 MapMessage map = session.createMapMessage();
2189                                 map.setObject("id", id);
2190                                 map.setBoolean("delete", false);
2191                                 sender.send(map);
2192                         }
2193                         sendOptimize(httpClient, 0);
2194                 } catch (DOMException e) {
2195                         throw new EJBException(e);
2196                 } catch (TransformerConfigurationException e) {
2197                         throw new EJBException(e);
2198                 } catch (IllegalArgumentException e) {
2199                         throw new EJBException(e);
2200                 } catch (HttpException e) {
2201                         throw new EJBException(e);
2202                 } catch (UnsupportedEncodingException e) {
2203                         throw new EJBException(e);
2204                 } catch (ParserConfigurationException e) {
2205                         throw new EJBException(e);
2206                 } catch (TransformerException e) {
2207                         throw new EJBException(e);
2208                 } catch (IOException e) {
2209                         throw new EJBException(e);
2210                 } catch (NamingException e) {
2211                         throw new EJBException(e);
2212                 } catch (JMSException e) {
2213                         throw new EJBException(e);
2214                 }
2215                 finally {
2216                         try {
2217                                 if (sender != null)
2218                                         sender.close();
2219                                 if (session != null)
2220                                         session.close();
2221                                 if (qConn != null)
2222                                         qConn.close();
2223                         }
2224                         catch (JMSException e) {
2225                                 logger.warn(e);
2226                         }
2227                 }
2228         }
2229
2230         /**
2231          * Sends a optimize message to the solr server
2232          *
2233          * @param httpClient
2234          * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive
2235          *                                      calls to stop the recursion
2236          * @throws UnsupportedEncodingException
2237          * @throws IOException
2238          * @throws HttpException
2239          */
2240         private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException {
2241                 PostMethod method = null;
2242                 try {
2243                         logger.debug("Optimize retry: " + retryCount);
2244                         method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2245                         method.setRequestEntity(new StringRequestEntity("<optimize/>", "text/xml", "iso8859-1"));
2246                         int statusCode = httpClient.executeMethod(method);
2247                         logger.debug("HTTP status: " + statusCode);
2248                         String response = method.getResponseBodyAsString();
2249                         logger.debug(response);
2250                         if (statusCode != 200 && retryCount < 2) {
2251                                 try {
2252                                         Thread.sleep(10000); //Give Solr a little time to be available
2253                                 } catch (InterruptedException e) {
2254                                 }
2255                                 sendOptimize(httpClient, retryCount + 1);
2256                         }
2257                 }
2258                 finally {
2259                         if (method != null)
2260                                 method.releaseConnection();
2261                 }
2262         }
2263
2264         @Override
2265         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2266                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2267                         InsufficientPermissionsException, QuotaExceededException {
2268                 // Validate.
2269                 if (userId == null)
2270                         throw new ObjectNotFoundException("No user specified");
2271                 if (folderId == null)
2272                         throw new ObjectNotFoundException("No folder specified");
2273                 String contentType = mimeType;
2274                 if (StringUtils.isEmpty(mimeType))
2275                         contentType = DEFAULT_MIME_TYPE;
2276                 if (StringUtils.isEmpty(name))
2277                         throw new ObjectNotFoundException("No file name specified");
2278                 if (dao.existsFolderOrFile(folderId, name))
2279                         throw new DuplicateNameException("A folder or file with the name '" + name +
2280                                                 "' already exists at this level");
2281
2282                 // Do the actual work.
2283                 Folder parent = null;
2284                 try {
2285                         parent = dao.getEntityById(Folder.class, folderId);
2286                 } catch (final ObjectNotFoundException onfe) {
2287                         // Supply a more accurate problem description.
2288                         throw new ObjectNotFoundException("Parent folder not found");
2289                 }
2290                 final User owner = dao.getEntityById(User.class, userId);
2291                 if (!parent.hasWritePermission(owner))
2292                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2293                 final FileHeader file = new FileHeader();
2294                 file.setName(name);
2295                 parent.addFile(file);
2296                 // set file owner to folder owner
2297                 file.setOwner(parent.getOwner());
2298                 //set file's readForAll value according to parent folder readForAll value
2299                 file.setReadForAll(parent.isReadForAll());
2300
2301                 final Date now = new Date();
2302                 final AuditInfo auditInfo = new AuditInfo();
2303                 auditInfo.setCreatedBy(owner);
2304                 auditInfo.setCreationDate(now);
2305                 auditInfo.setModifiedBy(owner);
2306                 auditInfo.setModificationDate(now);
2307                 file.setAuditInfo(auditInfo);
2308                 // TODO set the proper versioning flag on creation
2309                 file.setVersioned(false);
2310
2311                 for (final Permission p : parent.getPermissions()) {
2312                         final Permission permission = new Permission();
2313                         permission.setGroup(p.getGroup());
2314                         permission.setUser(p.getUser());
2315                         permission.setRead(p.getRead());
2316                         permission.setWrite(p.getWrite());
2317                         permission.setModifyACL(p.getModifyACL());
2318                         file.addPermission(permission);
2319                 }
2320
2321                 // Create the file body.
2322                 try {
2323                         createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2324                 } catch (FileNotFoundException e) {
2325                         throw new GSSIOException(e);
2326                 }
2327                 touchParentFolders(parent, owner, new Date());
2328                 dao.flush();
2329                 indexFile(file.getId(), false);
2330
2331                 return file.getDTO();
2332         }
2333
2334         @Override
2335         public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2336                 if (userId == null)
2337                         throw new ObjectNotFoundException("No user specified");
2338                 if (fileId == null)
2339                         throw new ObjectNotFoundException("No file specified");
2340                 String contentType = mimeType;
2341
2342                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2343
2344                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2345                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2346                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2347                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2348                         contentType = identifyMimeType(file.getName());
2349
2350                 final User owner = dao.getEntityById(User.class, userId);
2351                 if (!file.hasWritePermission(owner))
2352                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2353                 final Date now = new Date();
2354                 final AuditInfo auditInfo = new AuditInfo();
2355                 auditInfo.setCreatedBy(owner);
2356                 auditInfo.setCreationDate(now);
2357                 auditInfo.setModifiedBy(owner);
2358                 auditInfo.setModificationDate(now);
2359                 try {
2360                         createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2361                 } catch (FileNotFoundException e) {
2362                         throw new GSSIOException(e);
2363                 }
2364                 Folder parent = file.getFolder();
2365                 touchParentFolders(parent, owner, new Date());
2366
2367                 indexFile(fileId, false);
2368                 return file.getDTO();
2369         }
2370
2371         /**
2372          * Helper method for identifying mime type by examining the filename extension
2373          *
2374          * @param filename
2375          * @return the mime type
2376          */
2377         private String identifyMimeType(String filename) {
2378                 if (filename.indexOf('.') != -1) {
2379                         String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2380                         if (".doc".equals(extension))
2381                                 return "application/msword";
2382                         else if (".xls".equals(extension))
2383                                 return "application/vnd.ms-excel";
2384                         else if (".ppt".equals(extension))
2385                                 return "application/vnd.ms-powerpoint";
2386                         else if (".pdf".equals(extension))
2387                                 return "application/pdf";
2388                         else if (".gif".equals(extension))
2389                                 return "image/gif";
2390                         else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2391                                 return "image/jpeg";
2392                         else if (".tiff".equals(extension) || ".tif".equals(extension))
2393                                 return "image/tiff";
2394                         else if (".png".equals(extension))
2395                                 return "image/png";
2396                         else if (".bmp".equals(extension))
2397                                 return "image/bmp";
2398                 }
2399                 // when all else fails assign the default mime type
2400                 return DEFAULT_MIME_TYPE;
2401         }
2402
2403         /**
2404          * Helper method to create a new file body and attach it as the current body
2405          * of the provided file header.
2406          *
2407          * @param name the original file name
2408          * @param mimeType the content type
2409          * @param fileSize the uploaded file size
2410          * @param filePath the uploaded file full path
2411          * @param header the file header that will be associated with the new body
2412          * @param auditInfo the audit info
2413          * @param owner the owner of the file
2414          * @throws FileNotFoundException
2415          * @throws QuotaExceededException
2416          * @throws ObjectNotFoundException if the owner was not found
2417          */
2418         private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2419                                 FileHeader header, AuditInfo auditInfo)
2420                         throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2421
2422                 long currentTotalSize = 0;
2423                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2424                         currentTotalSize = header.getTotalSize();
2425                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2426                 if(quotaLeft < fileSize-currentTotalSize) {
2427                         // quota exceeded -> delete the file
2428                         deleteActualFile(filePath);
2429                         throw new QuotaExceededException("Not enough free space available");
2430                 }
2431
2432                 FileBody body = new FileBody();
2433
2434                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2435                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2436                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2437                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2438                         body.setMimeType(identifyMimeType(name));
2439                 else
2440                         body.setMimeType(mimeType);
2441                 body.setAuditInfo(auditInfo);
2442                 body.setFileSize(fileSize);
2443                 body.setOriginalFilename(name);
2444                 body.setStoredFilePath(filePath);
2445                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2446                 if(!header.isVersioned() && header.getCurrentBody() != null){
2447                         header.setCurrentBody(null);
2448                         if (header.getBodies() != null) {
2449                                 Iterator<FileBody> it = header.getBodies().iterator();
2450                                 while(it.hasNext()){
2451                                         FileBody bo = it.next();
2452                                         deleteActualFile(bo.getStoredFilePath());
2453                                         it.remove();
2454                                         dao.delete(bo);
2455                                 }
2456                         }
2457                 }
2458
2459                 dao.flush();
2460                 header.addBody(body);
2461                 header.setAuditInfo(auditInfo);
2462
2463                 dao.create(body);
2464         }
2465
2466
2467         @Override
2468         @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2469         public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2470                 if (userId == null)
2471                         throw new ObjectNotFoundException("No user specified");
2472                 User owner = dao.getEntityById(User.class, userId);
2473                 if(owner == null)
2474                         throw new ObjectNotFoundException("No user specified");
2475                 long start = 0, end = 0;
2476                 if (logger.isDebugEnabled())
2477                         start = System.currentTimeMillis();
2478                 File result = new File(generateRepositoryFilePath());
2479                 try {
2480                         final FileOutputStream output = new FileOutputStream(result);
2481                         final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2482                         int n = 0;
2483
2484                         while (-1 != (n = stream.read(buffer)))
2485                                 output.write(buffer, 0, n);
2486                         output.close();
2487                         stream.close();
2488                 } catch (IOException e) {
2489                         if (!result.delete())
2490                                 logger.warn("Could not delete " + result.getPath());
2491                         throw e;
2492                 }
2493                 if (logger.isDebugEnabled()) {
2494                         end = System.currentTimeMillis();
2495                         logger.debug("Time to upload: " + (end - start) + " (msec)");
2496                 }
2497                 return result;
2498         }
2499
2500
2501         @Override
2502         public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2503
2504                 if (userId == null)
2505                         throw new ObjectNotFoundException("No user specified");
2506                 User user = dao.getEntityById(User.class, userId);
2507                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2508                 if(status == null){
2509                         status = new FileUploadStatus();
2510                         status.setOwner(user);
2511                         status.setFilename(filename);
2512                         status.setBytesUploaded(bytesTransfered);
2513                         status.setFileSize(fileSize);
2514                         dao.create(status);
2515                 }
2516                 else{
2517                         status.setBytesUploaded(bytesTransfered);
2518                         status.setFileSize(fileSize);
2519                         dao.update(status);
2520                 }
2521
2522         }
2523
2524         @Override
2525         public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2526                 if (userId == null)
2527                         throw new ObjectNotFoundException("No user specified");
2528                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2529                 if(status != null)
2530                         dao.delete(status);
2531         }
2532
2533         @Override
2534         public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2535                 return dao.getFileUploadStatus(userId, fileName);
2536         }
2537
2538         @Override
2539         public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2540                 if (userId == null)
2541                         throw new ObjectNotFoundException("No user specified");
2542                 if (folderId == null)
2543                         throw new ObjectNotFoundException("No folder specified");
2544                 final User user = dao.getEntityById(User.class, userId);
2545                 final Folder folder = dao.getEntityById(Folder.class, folderId);
2546                 // Check permissions
2547                 if (!folder.hasReadPermission(user))
2548                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2549                 List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
2550                 if (folder.hasReadPermission(user))
2551                         for (Folder f : folder.getSubfolders())
2552                                 if (f.hasReadPermission(user) && !f.isDeleted())
2553                                         subfolders.add(f.getDTO());
2554                 FolderDTO result = folder.getDTO();
2555                 result.setSubfolders(subfolders);
2556                 return folder.getDTO();
2557         }
2558
2559         @Override
2560         public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2561                 if (userId == null)
2562                         throw new ObjectNotFoundException("No user specified");
2563                 if (folderId == null)
2564                         throw new ObjectNotFoundException("No folder specified");
2565                 User user = dao.getEntityById(User.class, callingUserId);
2566                 Folder folder = dao.getEntityById(Folder.class, folderId);
2567                 // Check permissions
2568                 if (!folder.hasReadPermission(user))
2569                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2570
2571                 FolderDTO result = folder.getDTO();
2572                 result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2573                 return result;
2574         }
2575
2576         @Override
2577         public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
2578                         throws ObjectNotFoundException, InsufficientPermissionsException {
2579                 if (userId == null)
2580                         throw new ObjectNotFoundException("No user specified");
2581                 if (fileId == null)
2582                         throw new ObjectNotFoundException("No file specified");
2583                 if (version < 1)
2584                         throw new ObjectNotFoundException("No valid version specified");
2585                 User user = dao.getEntityById(User.class, userId);
2586                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2587                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2588                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2589                 FileBody body = dao.getFileVersion(fileId, version);
2590                 return body.getDTO();
2591         }
2592
2593         @Override
2594         public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2595                 if (userId == null)
2596                         throw new ObjectNotFoundException("No user specified");
2597                 User user = dao.getEntityById(User.class, userId);
2598                 user.setAcceptedPolicy(isAccepted);
2599                 return user;
2600         }
2601
2602         @Override
2603         public void updateAccounting(User user, Date date, long bandwidthDiff) {
2604                 dao.updateAccounting(user, date, bandwidthDiff);
2605         }
2606
2607         @Override
2608         public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2609                 if (userId == null)
2610                         throw new ObjectNotFoundException("No user specified");
2611                 if (folderId == null)
2612                         throw new ObjectNotFoundException("No folder specified");
2613                 User user = dao.getEntityById(User.class, userId);
2614                 Folder folder = dao.getEntityById(Folder.class, folderId);
2615                 // Check permissions
2616                 if (!folder.hasReadPermission(user))
2617                         return false;
2618                 return true;
2619         }
2620
2621         @Override
2622         public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2623                 if (userId == null)
2624                         throw new ObjectNotFoundException("No user specified");
2625                 User user = dao.getEntityById(User.class, userId);
2626                 user.generateWebDAVPassword();
2627                 return user.getWebDAVPassword();
2628         }
2629
2630         @Override
2631         public Invitation findInvite(String code) {
2632                 if (code == null)
2633                         return null;
2634                 return dao.findInvite(code);
2635         }
2636
2637         @Override
2638         public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2639                 LDAPConnection lc = new LDAPConnection();
2640         LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2641         attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2642         attributeSet.add(new LDAPAttribute("uid", username));
2643         attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2644         attributeSet.add(new LDAPAttribute("sn", lastname));
2645         attributeSet.add(new LDAPAttribute("givenName", firstname));
2646         attributeSet.add(new LDAPAttribute("mail", email));
2647         attributeSet.add(new LDAPAttribute("userPassword", password));
2648         String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2649         LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2650         try {
2651                 lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2652                 lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2653                                 getConfiguration().getString("bindPassword").getBytes("UTF8"));
2654                 lc.add(newEntry);
2655                 logger.info("Successfully added LDAP account: " + dn);
2656                 lc.disconnect();
2657         } catch(LDAPException e) {
2658                 throw new RuntimeException(e);
2659         } catch(UnsupportedEncodingException e) {
2660                 throw new RuntimeException(e);
2661         }
2662
2663         }
2664
2665         @Override
2666         public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
2667                 User user = findUser(username);
2668                 if (user == null)
2669                         throw new ObjectNotFoundException("The user was not found");
2670                 Invitation invite = findInvite(code);
2671                 if (invite.getUser() != null)
2672                         throw new InvitationUsedException("This code has already been used");
2673                 invite.setUser(user);
2674                 UserClass couponClass = getCouponUserClass();
2675                 user.setUserClass(couponClass);
2676                 return couponClass;
2677         }
2678
2679         @Override
2680         public UserClass getCouponUserClass() {
2681                 return dao.findCouponUserClass();
2682         }
2683
2684         /**
2685          * Mark the folder as modified from the specified user and change it's modification date.
2686          */
2687         private void touchFolder(Folder f, User owner, Date now){
2688                 final AuditInfo auditInfo = new AuditInfo();
2689                 auditInfo.setCreatedBy(owner);
2690                 auditInfo.setCreationDate(now);
2691                 auditInfo.setModifiedBy(owner);
2692                 auditInfo.setModificationDate(now);
2693                 f.setAuditInfo(auditInfo);
2694         }
2695
2696         /**
2697          * Mark the file as modified from the specified user and change it's modification date.
2698          */
2699         private void touchFile(FileHeader f, User owner, Date now){
2700                 final AuditInfo auditInfo = new AuditInfo();
2701                 auditInfo.setCreatedBy(owner);
2702                 auditInfo.setCreationDate(now);
2703                 auditInfo.setModifiedBy(owner);
2704                 auditInfo.setModificationDate(now);
2705                 f.setAuditInfo(auditInfo);
2706         }
2707
2708         /**
2709          * Set the provided readForAll as the new readforAll value of the specified
2710          * folder and sub-folders.
2711          *
2712          * @param user
2713          * @param folder
2714          * @param readForAll
2715          * @throws ObjectNotFoundException
2716          *
2717          */
2718         private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
2719                 if (readForAll != null && user.equals(folder.getOwner())){
2720                         folder.setReadForAll(readForAll);
2721                         dao.update(folder);
2722                         for (FileHeader file : folder.getFiles())
2723                                 file.setReadForAll(readForAll);
2724                         if(readForAll)
2725                                 //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
2726                                 for (Folder sub : folder.getSubfolders())
2727                                         setFolderReadForAll(user, sub, readForAll);
2728
2729                 }
2730
2731         }
2732 }