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