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