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