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