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