Increased frequency of commits during solr index rebuild to every 10 files
[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 || (permissions != null && !permissions.isEmpty()) || readForAll != 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<FileHeader> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1790         long startTime = System.currentTimeMillis();
1791                 if (userId == null)
1792                         throw new ObjectNotFoundException("No user specified");
1793                 User user = getUser(userId);
1794                 if (query == null)
1795                         throw new ObjectNotFoundException("No query specified");
1796                 List<FileHeader> files = search(user.getId(), query);
1797                 
1798         long stopTime = System.currentTimeMillis();
1799         logger.info("Total time: " + (stopTime - startTime));
1800                 return files;
1801         }
1802
1803         /**
1804          * Performs the actuals search on the solr server and returns the results
1805          *
1806          * @param userId
1807          * @param query
1808          * @return a List of FileHeader objects
1809          */
1810         private List<FileHeader> search(Long userId, String query) {
1811         final int maxRows = 100;
1812                 List<FileHeader> result = new ArrayList<FileHeader>();
1813                 try {
1814                         CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
1815             List<Group> groups = dao.getGroupsContainingUser(userId);
1816             String constructedQuery = escapeCharacters(normalizeSearchQuery(query)) + " AND (public: true OR ureaders: " + userId;
1817             if (!groups.isEmpty()) {
1818                 constructedQuery += " OR (";
1819                 for (int i=0; i<groups.size(); i++) {
1820                     Group g = groups.get(i);
1821                     constructedQuery += "greaders :" + g.getId();
1822                     if (i < groups.size() - 1)
1823                         constructedQuery += " OR ";
1824                 }
1825                 constructedQuery += ")";
1826             }
1827             constructedQuery += ")";
1828                         SolrQuery solrQuery = new SolrQuery(constructedQuery);
1829             solrQuery.setRows(maxRows);
1830             long startTime = System.currentTimeMillis();
1831                         QueryResponse response = solr.query(solrQuery);
1832                         SolrDocumentList results = response.getResults();
1833             if (results.getNumFound() > maxRows) {
1834                 solrQuery.setRows(Integer.valueOf((int) results.getNumFound()));
1835                 response = solr.query(solrQuery);
1836                 results = response.getResults();
1837             }
1838             long stopTime = System.currentTimeMillis();
1839             logger.info("Search time:" +  (stopTime - startTime));
1840                         User user = getUser(userId);
1841             startTime = System.currentTimeMillis();
1842                         for (SolrDocument d : results) {
1843                                 Long id = Long.valueOf((String) d.getFieldValue("id"));
1844                                 try {
1845                                         FileHeader f = dao.getEntityById(FileHeader.class, id);
1846                                         result.add(f);
1847                                 } catch (ObjectNotFoundException e) {
1848                                         logger.warn("Search result id " + id + " cannot be found", e);
1849                                 }
1850                         }
1851             stopTime = System.currentTimeMillis();
1852             logger.info("File loads: " + (stopTime - startTime));
1853                 } catch (MalformedURLException e) {
1854                         logger.error(e);
1855                         throw new EJBException(e);
1856                 } catch (SolrServerException e) {
1857                         logger.error(e);
1858                         throw new EJBException(e);
1859                 } catch (ObjectNotFoundException e) {
1860                         logger.error(e);
1861                         throw new EJBException(e);
1862                 }
1863                 return result;
1864         }
1865
1866         @Override
1867         public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1868                 for(Long l : fileIds){
1869                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1870                         copyFile(userId, l, destId, file.getName());
1871                 }
1872
1873
1874         }
1875
1876         @Override
1877         public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1878                 for(Long l : fileIds){
1879                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1880                         moveFile(userId, l, destId, file.getName());
1881                 }
1882
1883         }
1884
1885         @Override
1886         public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1887                 if (userId == null)
1888                         throw new ObjectNotFoundException("No user specified");
1889                 final User user = dao.getEntityById(User.class, userId);
1890                 List<String> filesToRemove = new ArrayList<String>();
1891                 //first delete database objects
1892                 for(Long fileId : fileIds){
1893                         if (fileId == null)
1894                                 throw new ObjectNotFoundException("No file specified");
1895                         final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1896                         final Folder parent = file.getFolder();
1897                         if (parent == null)
1898                                 throw new ObjectNotFoundException("The specified file has no parent folder");
1899                         if (!file.hasDeletePermission(user))
1900                                 throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1901
1902                         parent.removeFile(file);
1903                         for (final FileBody body : file.getBodies())
1904                                 filesToRemove.add(body.getStoredFilePath());
1905                         dao.delete(file);
1906                         touchParentFolders(parent, user, new Date());
1907                 }
1908                 //then remove physical files if everything is ok
1909                 for(String physicalFileName : filesToRemove)
1910                         deleteActualFile(physicalFileName);
1911                 //then unindex deleted files
1912                 for(Long fileId : fileIds)
1913                         indexFile(fileId, true);
1914
1915         }
1916
1917         @Override
1918         public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1919                 for(Long l : fileIds)
1920                         moveFileToTrash(userId, l);
1921
1922         }
1923
1924         @Override
1925         public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1926                 for(Long l : fileIds)
1927                         removeFileFromTrash(userId, l);
1928
1929         }
1930
1931         @Override
1932         public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1933                 if (userId == null)
1934                         throw new ObjectNotFoundException("No user specified");
1935                 User user = dao.getEntityById(User.class, userId);
1936                 Nonce nonce = Nonce.createNonce(user.getId());
1937                 dao.create(nonce);
1938                 return nonce;
1939         }
1940
1941         @Override
1942         public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1943                 if (userId == null)
1944                         throw new ObjectNotFoundException("No user specified");
1945                 if (nonce == null)
1946                         throw new ObjectNotFoundException("No nonce specified");
1947                 return dao.getNonce(nonce, userId);
1948         }
1949
1950         @Override
1951         public void removeNonce(Long id) throws ObjectNotFoundException {
1952                 if (id == null)
1953                         throw new ObjectNotFoundException("No nonce specified");
1954                 Nonce nonce = dao.getEntityById(Nonce.class, id);
1955                 dao.delete(nonce);
1956         }
1957
1958         @Override
1959         public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1960                 if (userId == null)
1961                         throw new ObjectNotFoundException("No user specified");
1962                 User user = dao.getEntityById(User.class, userId);
1963                 user.setNonce(nonce);
1964                 user.setNonceExpiryDate(nonceExpiryDate);
1965         }
1966
1967         @Override
1968         public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
1969                 if (userId == null)
1970                         throw new ObjectNotFoundException("No user specified");
1971                 StatsDTO stats = new StatsDTO();
1972                 stats.setFileCount(dao.getFileCount(userId));
1973                 Long fileSize = dao.getFileSize(userId);
1974                 stats.setFileSize(fileSize);
1975                 Long quota = getQuota(userId);
1976                 Long quotaLeft = quota - fileSize;
1977                 stats.setQuotaLeftSize(quotaLeft);
1978                 return stats;
1979         }
1980
1981         @Override
1982         public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1983                 if (userId == null)
1984                         throw new ObjectNotFoundException("No user specified");
1985                 if (fileId == null)
1986                         throw new ObjectNotFoundException("No file specified");
1987                 User user = dao.getEntityById(User.class, userId);
1988                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1989                 if(!header.hasReadPermission(user))
1990                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1991                 List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
1992                 for(int i = header.getBodies().size()-1 ; i>=0; i--)
1993                         result.add(header.getBodies().get(i).getDTO());
1994                 return result;
1995         }
1996
1997         @Override
1998         public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
1999                 if (userId == null)
2000                         throw new ObjectNotFoundException("No user specified");
2001                 if (fileId == null)
2002                         throw new ObjectNotFoundException("No file specified");
2003                 if (bodyId == null)
2004                         throw new ObjectNotFoundException("No body specified");
2005                 User user = dao.getEntityById(User.class, userId);
2006                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2007                 if(!header.hasWritePermission(user))
2008                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2009                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
2010                 if(body.equals(header.getCurrentBody())){
2011
2012                         if(header.getBodies().size() == 1)
2013                                 throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
2014                         for(FileBody b : header.getBodies())
2015                                 if(b.getVersion() == body.getVersion()-1)
2016                                         header.setCurrentBody(b);
2017                 }
2018                 deleteActualFile(body.getStoredFilePath());
2019                 header.getBodies().remove(body);
2020
2021                 Folder parent = header.getFolder();
2022                 touchParentFolders(parent, user, new Date());
2023
2024         }
2025
2026         @Override
2027         public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
2028                 if (userId == null)
2029                         throw new ObjectNotFoundException("No user specified");
2030                 if (fileId == null)
2031                         throw new ObjectNotFoundException("No file specified");
2032                 User user = dao.getEntityById(User.class, userId);
2033                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2034                 if(!header.hasWritePermission(user))
2035                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2036                 FileBody body = dao.getFileVersion(fileId, version);
2037                 final File fileContents = new File(body.getStoredFilePath());
2038
2039                 try {
2040                         updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2041                 } catch (FileNotFoundException e) {
2042                         throw new GSSIOException(e);
2043                 }
2044
2045         }
2046
2047         /* (non-Javadoc)
2048          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
2049          */
2050         @Override
2051         public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2052                 if (userId == null)
2053                         throw new ObjectNotFoundException("No user specified");
2054                 if (fileId == null)
2055                         throw new ObjectNotFoundException("No file specified");
2056                 User user = dao.getEntityById(User.class, userId);
2057                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2058                 if(!header.hasWritePermission(user))
2059                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2060                 Iterator<FileBody> it = header.getBodies().iterator();
2061                 while(it.hasNext()){
2062                         FileBody body = it.next();
2063                         if(!body.equals(header.getCurrentBody())){
2064                                 deleteActualFile(body.getStoredFilePath());
2065                                 it.remove();
2066                                 dao.delete(body);
2067                         }
2068                 }
2069                 header.getCurrentBody().setVersion(1);
2070
2071                 Folder parent = header.getFolder();
2072                 touchParentFolders(parent, user, new Date());
2073         }
2074
2075         /**
2076          * Gets the quota left for specified user ID.
2077          */
2078         private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
2079                 Long fileSize = dao.getFileSize(userId);
2080                 Long quota = getQuota(userId);
2081                 return quota - fileSize;
2082         }
2083
2084         /**
2085          * Gets the quota for specified user ID.
2086          */
2087         private Long getQuota(Long userId) throws ObjectNotFoundException{
2088                 UserClass uc = getUser(userId).getUserClass();
2089                 if (uc == null)
2090                         uc = getDefaultUserClass();
2091                 return uc.getQuota();
2092         }
2093
2094         @Override
2095     @TransactionAttribute(TransactionAttributeType.NEVER)
2096         public String rebuildSolrIndex() {
2097                 try {
2098             CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2099                         solr.deleteByQuery("*:*");
2100                         solr.commit();
2101             logger.info("Deleted everything in solr");
2102
2103                         List<Long> fileIds = dao.getAllFileIds();
2104             logger.info("Total of " + fileIds.size() + " will be indexed");
2105             int i = 0;
2106                         for (Long id : fileIds) {
2107                                 postFileToSolr(solr, id);
2108                 i++;
2109                 if (i % 100 == 0) {
2110                     solr.commit();
2111                     logger.info("Sent commit to solr at file " + i);
2112                 }
2113                         }
2114                         solr.optimize();
2115                         solr.commit();
2116             logger.info("Finished indexing of " + i + " files");
2117             return "Finished indexing of " + i + " files";
2118                 } catch (IOException e) {
2119                         throw new EJBException(e);
2120                 } catch (SolrServerException e) {
2121                         throw new EJBException(e);
2122                 }
2123         }
2124
2125         @Override
2126     @TransactionAttribute(TransactionAttributeType.NEVER)
2127         public String refreshSolrIndex() {
2128                 try {
2129                         CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2130                         
2131                         List<Long> fileIds = dao.getAllFileIds();
2132             logger.info("Total of " + fileIds.size() + " will be indexed");
2133             int i = 0;
2134                         for (Long id : fileIds) {
2135                                 postFileToSolr(solr, id);
2136                 i++;
2137                         }
2138             if (i % 10 == 0) {
2139                 solr.commit();
2140                 logger.info("Sent commit to solr at file " + i);
2141             }
2142                         solr.optimize();
2143                         solr.commit();
2144             logger.info("Finished indexing of " + i + " files");
2145             return "Finished indexing of " + i + " files";
2146                 } catch (IOException e) {
2147                         throw new EJBException(e);
2148                 } catch (SolrServerException e) {
2149                         throw new EJBException(e);
2150                 }
2151         }
2152
2153         @Override
2154         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2155                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2156                         InsufficientPermissionsException, QuotaExceededException {
2157                 // Validate.
2158                 if (userId == null)
2159                         throw new ObjectNotFoundException("No user specified");
2160                 if (folderId == null)
2161                         throw new ObjectNotFoundException("No folder specified");
2162                 String contentType = mimeType;
2163                 if (StringUtils.isEmpty(mimeType))
2164                         contentType = DEFAULT_MIME_TYPE;
2165                 if (StringUtils.isEmpty(name))
2166                         throw new ObjectNotFoundException("No file name specified");
2167                 if (dao.existsFolderOrFile(folderId, name))
2168                         throw new DuplicateNameException("A folder or file with the name '" + name +
2169                                                 "' already exists at this level");
2170
2171                 // Do the actual work.
2172                 Folder parent = null;
2173                 try {
2174                         parent = dao.getEntityById(Folder.class, folderId);
2175                 } catch (final ObjectNotFoundException onfe) {
2176                         // Supply a more accurate problem description.
2177                         throw new ObjectNotFoundException("Parent folder not found");
2178                 }
2179                 final User owner = dao.getEntityById(User.class, userId);
2180                 if (!parent.hasWritePermission(owner))
2181                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2182                 final FileHeader file = new FileHeader();
2183                 file.setName(name);
2184                 parent.addFile(file);
2185                 // set file owner to folder owner
2186                 file.setOwner(parent.getOwner());
2187                 //set file's readForAll value according to parent folder readForAll value
2188                 file.setReadForAll(parent.isReadForAll());
2189
2190                 final Date now = new Date();
2191                 final AuditInfo auditInfo = new AuditInfo();
2192                 auditInfo.setCreatedBy(owner);
2193                 auditInfo.setCreationDate(now);
2194                 auditInfo.setModifiedBy(owner);
2195                 auditInfo.setModificationDate(now);
2196                 file.setAuditInfo(auditInfo);
2197                 // TODO set the proper versioning flag on creation
2198                 file.setVersioned(false);
2199
2200                 for (final Permission p : parent.getPermissions()) {
2201                         final Permission permission = new Permission();
2202                         permission.setGroup(p.getGroup());
2203                         permission.setUser(p.getUser());
2204                         permission.setRead(p.getRead());
2205                         permission.setWrite(p.getWrite());
2206                         permission.setModifyACL(p.getModifyACL());
2207                         file.addPermission(permission);
2208                 }
2209
2210                 // Create the file body.
2211                 try {
2212                         createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2213                 } catch (FileNotFoundException e) {
2214                         throw new GSSIOException(e);
2215                 }
2216                 touchParentFolders(parent, owner, new Date());
2217                 dao.flush();
2218                 indexFile(file.getId(), false);
2219
2220                 return file.getDTO();
2221         }
2222
2223         @Override
2224         public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2225                 if (userId == null)
2226                         throw new ObjectNotFoundException("No user specified");
2227                 if (fileId == null)
2228                         throw new ObjectNotFoundException("No file specified");
2229                 String contentType = mimeType;
2230
2231                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2232
2233                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2234                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2235                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2236                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2237                         contentType = identifyMimeType(file.getName());
2238
2239                 final User owner = dao.getEntityById(User.class, userId);
2240                 if (!file.hasWritePermission(owner))
2241                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2242                 final Date now = new Date();
2243                 final AuditInfo auditInfo = new AuditInfo();
2244                 auditInfo.setCreatedBy(owner);
2245                 auditInfo.setCreationDate(now);
2246                 auditInfo.setModifiedBy(owner);
2247                 auditInfo.setModificationDate(now);
2248                 try {
2249                         createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2250                 } catch (FileNotFoundException e) {
2251                         throw new GSSIOException(e);
2252                 }
2253                 Folder parent = file.getFolder();
2254                 touchParentFolders(parent, owner, new Date());
2255
2256                 indexFile(fileId, false);
2257                 return file.getDTO();
2258         }
2259
2260         /**
2261          * Helper method for identifying mime type by examining the filename extension
2262          *
2263          * @param filename
2264          * @return the mime type
2265          */
2266         private String identifyMimeType(String filename) {
2267                 if (filename.indexOf('.') != -1) {
2268                         String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2269                         if (".doc".equals(extension))
2270                                 return "application/msword";
2271                         else if (".xls".equals(extension))
2272                                 return "application/vnd.ms-excel";
2273                         else if (".ppt".equals(extension))
2274                                 return "application/vnd.ms-powerpoint";
2275                         else if (".pdf".equals(extension))
2276                                 return "application/pdf";
2277                         else if (".gif".equals(extension))
2278                                 return "image/gif";
2279                         else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2280                                 return "image/jpeg";
2281                         else if (".tiff".equals(extension) || ".tif".equals(extension))
2282                                 return "image/tiff";
2283                         else if (".png".equals(extension))
2284                                 return "image/png";
2285                         else if (".bmp".equals(extension))
2286                                 return "image/bmp";
2287                 }
2288                 // when all else fails assign the default mime type
2289                 return DEFAULT_MIME_TYPE;
2290         }
2291
2292         /**
2293          * Helper method to create a new file body and attach it as the current body
2294          * of the provided file header.
2295          *
2296          * @param name the original file name
2297          * @param mimeType the content type
2298          * @param fileSize the uploaded file size
2299          * @param filePath the uploaded file full path
2300          * @param header the file header that will be associated with the new body
2301          * @param auditInfo the audit info
2302          * @throws FileNotFoundException
2303          * @throws QuotaExceededException
2304          * @throws ObjectNotFoundException if the owner was not found
2305          */
2306         private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2307                                 FileHeader header, AuditInfo auditInfo)
2308                         throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2309
2310                 long currentTotalSize = 0;
2311                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2312                         currentTotalSize = header.getTotalSize();
2313                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2314                 if(quotaLeft < fileSize-currentTotalSize) {
2315                         // quota exceeded -> delete the file
2316                         deleteActualFile(filePath);
2317                         throw new QuotaExceededException("Not enough free space available");
2318                 }
2319
2320                 FileBody body = new FileBody();
2321
2322                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2323                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2324                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2325                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2326                         body.setMimeType(identifyMimeType(name));
2327                 else
2328                         body.setMimeType(mimeType);
2329                 body.setAuditInfo(auditInfo);
2330                 body.setFileSize(fileSize);
2331                 body.setOriginalFilename(name);
2332                 body.setStoredFilePath(filePath);
2333                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2334                 if(!header.isVersioned() && header.getCurrentBody() != null){
2335                         header.setCurrentBody(null);
2336                         if (header.getBodies() != null) {
2337                                 Iterator<FileBody> it = header.getBodies().iterator();
2338                                 while(it.hasNext()){
2339                                         FileBody bo = it.next();
2340                                         deleteActualFile(bo.getStoredFilePath());
2341                                         it.remove();
2342                                         dao.delete(bo);
2343                                 }
2344                         }
2345                 }
2346
2347                 dao.flush();
2348                 header.addBody(body);
2349                 header.setAuditInfo(auditInfo);
2350
2351                 dao.create(body);
2352         }
2353
2354
2355         @Override
2356         @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2357         public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2358                 if (userId == null)
2359                         throw new ObjectNotFoundException("No user specified");
2360                 User owner = dao.getEntityById(User.class, userId);
2361                 if(owner == null)
2362                         throw new ObjectNotFoundException("No user specified");
2363                 long start = 0, end = 0;
2364                 if (logger.isDebugEnabled())
2365                         start = System.currentTimeMillis();
2366                 File result = new File(generateRepositoryFilePath());
2367                 try {
2368                         final FileOutputStream output = new FileOutputStream(result);
2369                         final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2370                         int n = 0;
2371
2372                         while (-1 != (n = stream.read(buffer)))
2373                                 output.write(buffer, 0, n);
2374                         output.close();
2375                         stream.close();
2376                 } catch (IOException e) {
2377                         if (!result.delete())
2378                                 logger.warn("Could not delete " + result.getPath());
2379                         throw e;
2380                 }
2381                 if (logger.isDebugEnabled()) {
2382                         end = System.currentTimeMillis();
2383                         logger.debug("Time to upload: " + (end - start) + " (msec)");
2384                 }
2385                 return result;
2386         }
2387
2388
2389         @Override
2390         public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2391
2392                 if (userId == null)
2393                         throw new ObjectNotFoundException("No user specified");
2394                 User user = dao.getEntityById(User.class, userId);
2395                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2396                 if(status == null){
2397                         status = new FileUploadStatus();
2398                         status.setOwner(user);
2399                         status.setFilename(filename);
2400                         status.setBytesUploaded(bytesTransfered);
2401                         status.setFileSize(fileSize);
2402                         dao.create(status);
2403                 }
2404                 else{
2405                         status.setBytesUploaded(bytesTransfered);
2406                         status.setFileSize(fileSize);
2407                         dao.update(status);
2408                 }
2409
2410         }
2411
2412         @Override
2413         public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2414                 if (userId == null)
2415                         throw new ObjectNotFoundException("No user specified");
2416                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2417                 if(status != null)
2418                         dao.delete(status);
2419         }
2420
2421         @Override
2422         public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2423                 return dao.getFileUploadStatus(userId, fileName);
2424         }
2425
2426         @Override
2427         public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2428                 if (userId == null)
2429                         throw new ObjectNotFoundException("No user specified");
2430                 if (folderId == null)
2431                         throw new ObjectNotFoundException("No folder specified");
2432                 final User user = dao.getEntityById(User.class, userId);
2433                 final Folder folder = dao.getEntityById(Folder.class, folderId);
2434                 // Check permissions
2435                 if (!folder.hasReadPermission(user))
2436                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2437                 List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
2438                 if (folder.hasReadPermission(user))
2439                         for (Folder f : folder.getSubfolders())
2440                                 if (f.hasReadPermission(user) && !f.isDeleted())
2441                                         subfolders.add(f.getDTO());
2442                 FolderDTO result = folder.getDTO();
2443                 result.setSubfolders(subfolders);
2444                 return folder.getDTO();
2445         }
2446
2447         @Override
2448         public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2449                 if (userId == null)
2450                         throw new ObjectNotFoundException("No user specified");
2451                 if (folderId == null)
2452                         throw new ObjectNotFoundException("No folder specified");
2453                 User user = dao.getEntityById(User.class, callingUserId);
2454                 Folder folder = dao.getEntityById(Folder.class, folderId);
2455                 // Check permissions
2456                 if (!folder.hasReadPermission(user))
2457                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2458
2459                 FolderDTO result = folder.getDTO();
2460                 result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2461                 return result;
2462         }
2463
2464         @Override
2465         public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
2466                         throws ObjectNotFoundException, InsufficientPermissionsException {
2467                 if (userId == null)
2468                         throw new ObjectNotFoundException("No user specified");
2469                 if (fileId == null)
2470                         throw new ObjectNotFoundException("No file specified");
2471                 if (version < 1)
2472                         throw new ObjectNotFoundException("No valid version specified");
2473                 User user = dao.getEntityById(User.class, userId);
2474                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2475                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2476                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2477                 FileBody body = dao.getFileVersion(fileId, version);
2478                 return body.getDTO();
2479         }
2480
2481         @Override
2482         public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2483                 if (userId == null)
2484                         throw new ObjectNotFoundException("No user specified");
2485                 User user = dao.getEntityById(User.class, userId);
2486                 user.setAcceptedPolicy(isAccepted);
2487                 return user;
2488         }
2489
2490         @Override
2491         public void updateAccounting(User user, Date date, long bandwidthDiff) {
2492                 dao.updateAccounting(user, date, bandwidthDiff);
2493         }
2494
2495         @Override
2496         public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2497                 if (userId == null)
2498                         throw new ObjectNotFoundException("No user specified");
2499                 if (folderId == null)
2500                         throw new ObjectNotFoundException("No folder specified");
2501                 User user = dao.getEntityById(User.class, userId);
2502                 Folder folder = dao.getEntityById(Folder.class, folderId);
2503                 // Check permissions
2504                 if (!folder.hasReadPermission(user))
2505                         return false;
2506                 return true;
2507         }
2508
2509         @Override
2510         public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2511                 if (userId == null)
2512                         throw new ObjectNotFoundException("No user specified");
2513                 User user = dao.getEntityById(User.class, userId);
2514                 user.generateWebDAVPassword();
2515                 return user.getWebDAVPassword();
2516         }
2517
2518         @Override
2519         public Invitation findInvite(String code) {
2520                 if (code == null)
2521                         return null;
2522                 return dao.findInvite(code);
2523         }
2524
2525         @Override
2526         public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2527                 LDAPConnection lc = new LDAPConnection();
2528         LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2529         attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2530         attributeSet.add(new LDAPAttribute("uid", username));
2531         attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2532         attributeSet.add(new LDAPAttribute("sn", lastname));
2533         attributeSet.add(new LDAPAttribute("givenName", firstname));
2534         attributeSet.add(new LDAPAttribute("mail", email));
2535         attributeSet.add(new LDAPAttribute("userPassword", password));
2536         String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2537         LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2538         try {
2539                 lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2540                 lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2541                                 getConfiguration().getString("bindPassword").getBytes("UTF8"));
2542                 lc.add(newEntry);
2543                 logger.info("Successfully added LDAP account: " + dn);
2544                 lc.disconnect();
2545         } catch(LDAPException e) {
2546                 throw new RuntimeException(e);
2547         } catch(UnsupportedEncodingException e) {
2548                 throw new RuntimeException(e);
2549         }
2550
2551         }
2552
2553         @Override
2554         public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
2555                 User user = findUser(username);
2556                 if (user == null)
2557                         throw new ObjectNotFoundException("The user was not found");
2558                 Invitation invite = findInvite(code);
2559                 if (invite.getUser() != null)
2560                         throw new InvitationUsedException("This code has already been used");
2561                 invite.setUser(user);
2562                 UserClass couponClass = getCouponUserClass();
2563                 user.setUserClass(couponClass);
2564                 return couponClass;
2565         }
2566
2567         @Override
2568         public UserClass getCouponUserClass() {
2569                 return dao.findCouponUserClass();
2570         }
2571
2572         /**
2573          * Mark the folder as modified from the specified user and change it's modification date.
2574          */
2575         private void touchFolder(Folder f, User _user, Date now){
2576                 final AuditInfo auditInfo = f.getAuditInfo();
2577                 auditInfo.setModificationDate(now);
2578                 auditInfo.setModifiedBy(_user);
2579                 f.setAuditInfo(auditInfo);
2580         }
2581
2582         /**
2583          * Mark the file as modified from the specified user and change it's modification date.
2584          */
2585         private void touchFile(FileHeader f, User _user, Date now){
2586                 final AuditInfo auditInfo = f.getAuditInfo();
2587                 auditInfo.setModificationDate(now);
2588                 auditInfo.setModifiedBy(_user);
2589                 f.setAuditInfo(auditInfo);
2590         }
2591
2592         /**
2593          * Set the provided readForAll as the new readforAll value of the specified
2594          * folder and sub-folders.
2595          *
2596          * @param user
2597          * @param folder
2598          * @param readForAll
2599          * @throws ObjectNotFoundException
2600          *
2601          */
2602         private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
2603                 if (readForAll != null && user.equals(folder.getOwner())){
2604                         folder.setReadForAll(readForAll);
2605                         dao.update(folder);
2606                         for (FileHeader file : folder.getFiles())
2607                                 file.setReadForAll(readForAll);
2608                         if(readForAll)
2609                                 //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
2610                                 for (Folder sub : folder.getSubfolders())
2611                                         setFolderReadForAll(user, sub, readForAll);
2612
2613                 }
2614
2615         }
2616                 
2617         /**
2618          * Update the userLogin with the values from the supplied object.
2619          */
2620         
2621         public void addUserLogin(UserLogin userLogin) {
2622                 dao.update(userLogin);          
2623
2624         }
2625                 
2626         /**
2627          * Retrieves the current session user login and the user's last login
2628          * 
2629          * @param userId
2630          * @return a list of last two user logins
2631          * @throws ObjectNotFoundException 
2632          */
2633         
2634         public List<UserLogin> getUserLogins(Long userId) throws ObjectNotFoundException{
2635                 List<UserLogin> userLoginResults = new ArrayList<UserLogin>();          
2636                 userLoginResults = dao.getLoginsForUser(userId);        
2637                 if(userLoginResults.size() == 0)
2638                         throw new ObjectNotFoundException("No userlogin found for the user");
2639                 //if the user logins for the first time lastLoginDate = currentLoginDate
2640                 if(userLoginResults.size()==1)
2641                         userLoginResults.add(userLoginResults.get(0));
2642                 return userLoginResults;
2643         }
2644         
2645
2646         @Override
2647         public void postFileToSolr(CommonsHttpSolrServer solr, Long id) {
2648                 try {
2649                         FileHeader file = dao.getFileForIndexing(id);
2650                         FileBody body = file.getCurrentBody();
2651                         String mime = body.getMimeType();
2652                         boolean multipart = true;
2653                         if (!mime.equals("application/pdf") 
2654                                                 && !mime.equals("text/plain")
2655                                                 && !mime.equals("text/html")
2656                                                 && !mime.endsWith("msword")
2657                                                 && !mime.endsWith("ms-excel")
2658                                                 && !mime.endsWith("powerpoint")
2659                                                 || (body.getFileSize() > getConfiguration().getLong("solrDocumentUploadLimitInKB") * 1024))
2660                                 multipart = false;
2661
2662                         if (!multipart)
2663                                 sendMetaDataOnly(solr, file);
2664                         else {
2665                 ContentStreamUpdateRequest solrRequest = new ContentStreamUpdateRequest(getConfiguration().getString("solr.rich.update.path"));
2666                                 solrRequest.setParam("literal.id", file.getId().toString());
2667                                 solrRequest.setParam("literal.name", file.getName());
2668                                 for (FileTag t : file.getFileTags()) {
2669                                         solrRequest.getParams().add("literal.tag", t.getTag());
2670                                 }
2671                 for (Permission p : file.getPermissions()) {
2672                     if (p.getRead()) {
2673                         if (p.getUser() != null)
2674                             solrRequest.setParam("literal.ureaders", p.getUser().getId().toString());
2675                         else if (p.getGroup() != null)
2676                             solrRequest.setParam("literal.greaders", p.getGroup().getId().toString());
2677                     }
2678                 }
2679                 solrRequest.setParam("literal.owner", file.getOwner().getId().toString());
2680                 solrRequest.setParam("literal.public", String.valueOf(file.isReadForAll()));
2681                 File fsFile = new File(body.getStoredFilePath());
2682                                 solrRequest.addFile(fsFile);
2683                                 try {
2684                                         solr.request(solrRequest);
2685                                 }
2686                                 catch (SolrException e) {
2687                                         logger.warn("File " + id + " failed with SolrException: " + e.getLocalizedMessage() + ". Retrying without the file");
2688                                         //Let 's try without the file
2689                                         sendMetaDataOnly(solr, file);
2690                                 }
2691                                 catch (NullPointerException e) {
2692                                         logger.warn("File " + id + " failed with NullPointerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2693                                         //Let 's try without the file
2694                                         sendMetaDataOnly(solr, file);
2695                                 }
2696                                 catch (SolrServerException e) {
2697                                         logger.warn("File " + id + " failed with SolrServerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2698                                         //Let 's try without the file
2699                                         sendMetaDataOnly(solr, file);
2700                                 }
2701                         }
2702                 } catch (MalformedURLException e) {
2703                         throw new EJBException(e);
2704                 } catch (ObjectNotFoundException e) {
2705                         logger.error("Indexing of file id " + id + " failed.", e);
2706                 } catch (SolrServerException e) {
2707                         throw new EJBException(e);
2708                 } catch (IOException e) {
2709                         throw new EJBException(e);
2710                 }
2711         }
2712
2713         private void sendMetaDataOnly(CommonsHttpSolrServer solr, FileHeader file) throws SolrServerException, IOException {
2714                 SolrInputDocument solrDoc = new SolrInputDocument();
2715                 solrDoc.addField("id", file.getId().toString());
2716                 solrDoc.addField("name", file.getName());
2717                 for (FileTag t : file.getFileTags()) {
2718                         solrDoc.addField("tag", t.getTag());
2719                 }
2720         for (Permission p : file.getPermissions()) {
2721             if (p.getRead()) {
2722                 if (p.getUser() != null)
2723                     solrDoc.addField("ureaders", p.getUser().getId());
2724                 else if (p.getGroup() != null)
2725                     solrDoc.addField("greaders", p.getGroup().getId());
2726             }
2727         }
2728         solrDoc.addField("owner", file.getOwner().getId());
2729         solrDoc.addField("public", file.isReadForAll());
2730                 solr.add(solrDoc);
2731         }
2732
2733         private String tokenizeFilename(String filename){
2734                 StringBuffer result = new StringBuffer();
2735                 StringTokenizer tokenizer = new StringTokenizer(filename,"._");
2736                 while(tokenizer.hasMoreTokens()){
2737                         result.append(tokenizer.nextToken());
2738                         result.append(" ");
2739                 }
2740                 result.append(filename);
2741                 return result.toString();
2742         }
2743
2744         private String normalizeSearchQuery(String query) {
2745                 if (query.contains("*"))
2746                         return query.toLowerCase().replace('ά', 'α').replace('έ', 'ε').replace('ί', 'ι').replace('ή', 'η').replace('ύ', 'υ')
2747                                         .replace('ό', 'ο').replace('ς', 'σ').replace('ώ', 'ω').replace('ϊ', 'ι').replace('ϋ', 'υ');
2748                 else
2749                         return query;
2750         }
2751         
2752         private String escapeCharacters(String text) {
2753                 return text.replaceAll(":", "\\\\:");
2754         }
2755 }