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