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