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