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