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