Set all required objectClass attributes when adding an entry to LDAP. Also consistent...
[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.ObjectNotFoundException;
26 import gr.ebs.gss.client.exceptions.QuotaExceededException;
27 import gr.ebs.gss.server.domain.AuditInfo;
28 import gr.ebs.gss.server.domain.FileBody;
29 import gr.ebs.gss.server.domain.FileHeader;
30 import gr.ebs.gss.server.domain.FileTag;
31 import gr.ebs.gss.server.domain.FileUploadStatus;
32 import gr.ebs.gss.server.domain.Folder;
33 import gr.ebs.gss.server.domain.Group;
34 import gr.ebs.gss.server.domain.Invitation;
35 import gr.ebs.gss.server.domain.Nonce;
36 import gr.ebs.gss.server.domain.Permission;
37 import gr.ebs.gss.server.domain.User;
38 import gr.ebs.gss.server.domain.dto.FileBodyDTO;
39 import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
40 import gr.ebs.gss.server.domain.dto.FolderDTO;
41 import gr.ebs.gss.server.domain.dto.GroupDTO;
42 import gr.ebs.gss.server.domain.dto.PermissionDTO;
43 import gr.ebs.gss.server.domain.dto.StatsDTO;
44 import gr.ebs.gss.server.domain.dto.UserDTO;
45
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.FileOutputStream;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.io.StringWriter;
53 import java.io.UnsupportedEncodingException;
54 import java.util.ArrayList;
55 import java.util.Date;
56 import java.util.Iterator;
57 import java.util.LinkedHashSet;
58 import java.util.LinkedList;
59 import java.util.List;
60 import java.util.Locale;
61 import java.util.Random;
62 import java.util.Set;
63 import java.util.StringTokenizer;
64
65 import javax.ejb.EJB;
66 import javax.ejb.EJBException;
67 import javax.ejb.EJBTransactionRolledbackException;
68 import javax.ejb.Stateless;
69 import javax.ejb.TransactionAttribute;
70 import javax.ejb.TransactionAttributeType;
71 import javax.jms.Connection;
72 import javax.jms.ConnectionFactory;
73 import javax.jms.JMSException;
74 import javax.jms.MapMessage;
75 import javax.jms.MessageProducer;
76 import javax.jms.Queue;
77 import javax.jms.QueueConnectionFactory;
78 import javax.jms.Session;
79 import javax.naming.Context;
80 import javax.naming.InitialContext;
81 import javax.naming.NamingException;
82 import javax.persistence.PersistenceException;
83 import javax.xml.parsers.DocumentBuilder;
84 import javax.xml.parsers.DocumentBuilderFactory;
85 import javax.xml.parsers.ParserConfigurationException;
86 import javax.xml.transform.OutputKeys;
87 import javax.xml.transform.Transformer;
88 import javax.xml.transform.TransformerConfigurationException;
89 import javax.xml.transform.TransformerException;
90 import javax.xml.transform.TransformerFactory;
91 import javax.xml.transform.dom.DOMSource;
92 import javax.xml.transform.stream.StreamResult;
93
94 import org.apache.commons.httpclient.HttpClient;
95 import org.apache.commons.httpclient.HttpException;
96 import org.apache.commons.httpclient.NameValuePair;
97 import org.apache.commons.httpclient.methods.GetMethod;
98 import org.apache.commons.httpclient.methods.PostMethod;
99 import org.apache.commons.httpclient.methods.StringRequestEntity;
100 import org.apache.commons.lang.StringUtils;
101 import org.apache.commons.logging.Log;
102 import org.apache.commons.logging.LogFactory;
103 import org.hibernate.exception.ConstraintViolationException;
104 import org.w3c.dom.DOMException;
105 import org.w3c.dom.Document;
106 import org.w3c.dom.Node;
107 import org.w3c.dom.NodeList;
108 import org.xml.sax.SAXException;
109
110 import com.novell.ldap.LDAPAttribute;
111 import com.novell.ldap.LDAPAttributeSet;
112 import com.novell.ldap.LDAPConnection;
113 import com.novell.ldap.LDAPEntry;
114 import com.novell.ldap.LDAPException;
115
116 /**
117  * The concrete implementation of the ExternalAPI interface.
118  *
119  * @author past
120  */
121 @Stateless
122 public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
123         /**
124          * The default MIME type for files without an explicit one.
125          */
126         private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
127
128         /**
129          * The size of the buffer that is used to temporarily store chunks of
130          * uploaded files, while storing them to the file repository.
131          */
132         private static final int UPLOAD_BUFFER_SIZE = 1024 * 4;
133
134         /**
135          * The logger.
136          */
137         private static Log logger = LogFactory.getLog(ExternalAPIBean.class);
138
139         /**
140          * Injected reference to the GSSDAO data access facade.
141          */
142         @EJB
143         private GSSDAO dao;
144
145
146         /**
147          * A cached random number generator for creating unique filenames.
148          */
149         private static Random random = new Random();
150
151         /**
152          * Mark the folder and all of its parent folders as modified from the specified user.
153          */
154         private void touchParentFolders(Folder folder, User user, Date date) {
155                 Folder f = folder;
156                 while (f != null) {
157                         AuditInfo ai = f.getAuditInfo();
158                         ai.setModifiedBy(user);
159                         ai.setModificationDate(date);
160                         f.setAuditInfo(ai);
161                         f = f.getParent();
162                 }
163         }
164
165         @Override
166         public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException {
167                 if (userId == null)
168                         throw new ObjectNotFoundException("No user specified");
169                 Folder folder = dao.getRootFolder(userId);
170                 return folder.getDTO();
171         }
172
173         @Override
174         public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
175                 if (userId == null)
176                         throw new ObjectNotFoundException("No user specified");
177                 if (folderId == null)
178                         throw new ObjectNotFoundException("No folder specified");
179                 final User user = dao.getEntityById(User.class, userId);
180                 final Folder folder = dao.getEntityById(Folder.class, folderId);
181                 // Check permissions
182                 if (!folder.hasReadPermission(user))
183                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
184                 return folder.getDTO();
185         }
186
187         /* (non-Javadoc)
188          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUser(java.lang.Long)
189          */
190         public User getUser(Long userId) throws ObjectNotFoundException {
191                 if (userId == null)
192                         throw new ObjectNotFoundException("No user specified");
193                 return dao.getEntityById(User.class, userId);
194         }
195
196         /* (non-Javadoc)
197          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserDTO(java.lang.Long)
198          */
199         public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException {
200                 return getUser(userId).getDTO();
201         }
202
203         /*
204          * (non-Javadoc)
205          *
206          * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroup(java.lang.Long)
207          */
208         public GroupDTO getGroup(final Long groupId) throws ObjectNotFoundException {
209                 if (groupId == null)
210                         throw new ObjectNotFoundException("No group specified");
211                 final Group group = dao.getEntityById(Group.class, groupId);
212                 return group.getDTO();
213         }
214
215         @Override
216         public GroupDTO getGroup(Long userId, String name) throws ObjectNotFoundException {
217                 if (userId == null)
218                         throw new ObjectNotFoundException("No user specified");
219                 if (name == null)
220                         throw new ObjectNotFoundException("No group specified");
221                 User user = dao.getEntityById(User.class, userId);
222                 List<Group> groups = user.getGroupsSpecified();
223                 for (Group group: groups)
224                         if (group.getName().equals(name))
225                                 return group.getDTO();
226                 throw new ObjectNotFoundException("Group " + name + " not found");
227         }
228
229         /*
230          * (non-Javadoc)
231          *
232          * @see gr.ebs.gss.server.ejb.ExternalAPI#getGroups(java.lang.Long)
233          */
234         public List<GroupDTO> getGroups(final Long userId) throws ObjectNotFoundException {
235                 if (userId == null)
236                         throw new ObjectNotFoundException("No user specified");
237                 final List<Group> groups = dao.getGroups(userId);
238                 final List<GroupDTO> result = new ArrayList<GroupDTO>();
239                 for (final Group g : groups)
240                         result.add(g.getDTO());
241                 return result;
242         }
243
244         @Override
245         public List<FileHeaderDTO> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
246                         throws ObjectNotFoundException, InsufficientPermissionsException {
247                 // Validate.
248                 if (userId == null)
249                         throw new ObjectNotFoundException("No user specified");
250                 if (folderId == null)
251                         throw new ObjectNotFoundException("No folder specified");
252                 User user = dao.getEntityById(User.class, userId);
253                 Folder folder = dao.getEntityById(Folder.class, folderId);
254                 if (!folder.hasReadPermission(user))
255                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
256                 // Do the actual work.
257                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
258                 List<FileHeader> files = dao.getFiles(folderId, userId, ignoreDeleted);
259                 for (FileHeader f : files)
260                         result.add(f.getDTO());
261                 return result;
262         }
263
264         /*
265          * (non-Javadoc)
266          *
267          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsers(java.lang.Long,
268          *      java.lang.Long)
269          */
270         public List<UserDTO> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
271                 // Validate.
272                 if (userId == null)
273                         throw new ObjectNotFoundException("No user specified");
274                 if (groupId == null)
275                         throw new ObjectNotFoundException("No group specified");
276
277                 // Do the actual work.
278                 final List<User> users = dao.getUsers(groupId);
279                 final List<UserDTO> result = new ArrayList<UserDTO>();
280                 for (final User u : users)
281                         result.add(u.getDTO());
282                 return result;
283         }
284
285         @Override
286         public FolderDTO createFolder(Long userId, Long parentId, String name)
287                         throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
288                 // Validate.
289                 if (userId == null)
290                         throw new ObjectNotFoundException("No user specified");
291                 if (StringUtils.isEmpty(name))
292                         throw new ObjectNotFoundException("New folder name is empty");
293                 if (parentId == null)
294                         throw new ObjectNotFoundException("No parent specified");
295                 if (dao.existsFolderOrFile(parentId, name))
296                         throw new DuplicateNameException("A folder or file with the name '" +
297                                                 name + "' already exists at this level");
298
299                 User creator = dao.getEntityById(User.class, userId);
300
301                 Folder parent = null;
302                 try {
303                         parent = dao.getEntityById(Folder.class, parentId);
304                 } catch (ObjectNotFoundException onfe) {
305                         // Supply a more accurate problem description.
306                         throw new ObjectNotFoundException("Parent folder not found");
307                 }
308                 if (!parent.hasWritePermission(creator))
309                         throw new InsufficientPermissionsException("You don't have the permissions" +
310                                         " to write to this folder");
311
312                 // Do the actual work.
313                 return createFolder(name, parent, creator);
314         }
315
316         /**
317          * Create a new folder with the provided name, parent and owner.
318          *
319          * @param name
320          * @param parent
321          * @param creator
322          * @return the new folder
323          */
324         private FolderDTO createFolder(String name, Folder parent, User creator) {
325                 Folder folder = new Folder();
326                 folder.setName(name);
327                 if (parent != null) {
328                         parent.addSubfolder(folder);
329                         folder.setOwner(parent.getOwner());
330                 } else
331                         folder.setOwner(creator);
332
333                 Date now = new Date();
334                 AuditInfo auditInfo = new AuditInfo();
335                 auditInfo.setCreatedBy(creator);
336                 auditInfo.setCreationDate(now);
337                 auditInfo.setModifiedBy(creator);
338                 auditInfo.setModificationDate(now);
339                 folder.setAuditInfo(auditInfo);
340                 touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
341
342                 if (parent != null)
343                         for (Permission p : parent.getPermissions()) {
344                                 Permission permission = new Permission();
345                                 permission.setGroup(p.getGroup());
346                                 permission.setUser(p.getUser());
347                                 permission.setRead(p.getRead());
348                                 permission.setWrite(p.getWrite());
349                                 permission.setModifyACL(p.getModifyACL());
350                                 folder.addPermission(permission);
351                         }
352                 else {
353                         Permission permission = new Permission();
354                         permission.setUser(creator);
355                         permission.setRead(true);
356                         permission.setWrite(true);
357                         permission.setModifyACL(true);
358                         folder.addPermission(permission);
359                 }
360                 dao.create(folder);
361                 return folder.getDTO();
362         }
363
364         /*
365          * Deletes the given folder and all its subfolders and files
366          * Only the permissions for top folder are checked
367          *
368          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFolder(java.lang.Long,
369          *      java.lang.Long)
370          */
371         public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException {
372                 // Validate.
373                 if (userId == null)
374                         throw new ObjectNotFoundException("No user specified");
375                 if (folderId == null)
376                         throw new ObjectNotFoundException("No folder specified");
377
378                 // Do the actual work.
379                 final Folder folder = dao.getEntityById(Folder.class, folderId);
380                 final Folder parent = folder.getParent();
381                 if (parent == null)
382                         throw new ObjectNotFoundException("Deleting the root folder is not allowed");
383                 final User user = dao.getEntityById(User.class, userId);
384                 if (!folder.hasDeletePermission(user)) {
385                         logger.info("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
386                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
387                 }
388                 removeSubfolderFiles(folder);
389                 parent.removeSubfolder(folder);
390                 dao.delete(folder);
391                 touchParentFolders(parent, user, new Date());
392         }
393
394         /**
395          * Traverses the folder and deletes all actual files (file system)
396          * regardless of permissions
397          *
398          * @param folder
399          */
400         private void removeSubfolderFiles(Folder folder) {
401                 //remove files for all subfolders
402                 for (Folder subfolder:folder.getSubfolders())
403                         removeSubfolderFiles(subfolder);
404                 //remove this folder's file bodies (actual files)
405                 for (FileHeader file:folder.getFiles()) {
406                         for (FileBody body:file.getBodies())
407                                 deleteActualFile(body.getStoredFilePath());
408                         indexFile(file.getId(), true);
409                 }
410         }
411
412         @SuppressWarnings("unchecked")
413         public List<FolderDTO> getSubfolders(Long userId, Long folderId)
414                         throws ObjectNotFoundException, InsufficientPermissionsException {
415                 if (userId == null)
416                         throw new ObjectNotFoundException("No user specified");
417                 if (folderId == null)
418                         throw new ObjectNotFoundException("No folder specified");
419                 User user = dao.getEntityById(User.class, userId);
420                 Folder folder = dao.getEntityById(Folder.class, folderId);
421                 if (!folder.hasReadPermission(user))
422                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
423                 List<FolderDTO> result = new ArrayList<FolderDTO>();
424                 if (folder.hasReadPermission(user))
425                         for (Folder f : folder.getSubfolders())
426                                 if (f.hasReadPermission(user) && !f.isDeleted())
427                                         result.add(f.getDTO());
428                 return result;
429         }
430
431         @Override
432         public FolderDTO updateFolder(Long userId, Long folderId, String folderName,
433                                 Set<PermissionDTO> permissions)
434                         throws InsufficientPermissionsException, ObjectNotFoundException,
435                         DuplicateNameException {
436
437                 // Validate.
438                 if (userId == null)
439                         throw new ObjectNotFoundException("No user specified");
440                 if (folderId == null)
441                         throw new ObjectNotFoundException("No folder specified");
442
443                 Folder folder = dao.getEntityById(Folder.class, folderId);
444                 User user = dao.getEntityById(User.class, userId);
445                 if (folderName != null && !folder.hasWritePermission(user))
446                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
447                 if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
448                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
449
450                 Folder parent = folder.getParent();
451                 if (folderName != null) {
452                         if (parent != null)
453                                 if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
454                                         throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
455
456                         // Do the actual modification.
457                         folder.setName(folderName);
458                 }
459                 if (permissions != null)
460                         setFolderPermissions(user, folder, permissions);
461
462                 folder.getAuditInfo().setModificationDate(new Date());
463                 folder.getAuditInfo().setModifiedBy(user);
464                 dao.update(folder);
465                 touchParentFolders(folder, user, new Date());
466                 return folder.getDTO();
467         }
468
469         /*
470          * (non-Javadoc)
471          *
472          * @see gr.ebs.gss.server.ejb.ExternalAPI#createGroup(java.lang.Long,
473          *      java.lang.String)
474          */
475         public void createGroup(final Long userId, final String name) throws ObjectNotFoundException, DuplicateNameException {
476                 // Validate.
477                 if (userId == null)
478                         throw new ObjectNotFoundException("No user specified");
479                 if (StringUtils.isEmpty(name))
480                         throw new ObjectNotFoundException("New group name is empty");
481                 if (name.indexOf('/')>=0)
482                         throw new IllegalArgumentException("Character '/' is not allowed in group name");
483                 if (dao.existsGroup(userId, name))
484                         throw new DuplicateNameException("A group with the name '" + name + "' already exists");
485
486                 // TODO: Check permissions
487
488                 final User owner = dao.getEntityById(User.class, userId);
489
490                 // Do the actual work.
491                 owner.createGroup(name);
492         }
493
494         /*
495          * (non-Javadoc)
496          *
497          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteGroup(java.lang.Long,
498          *      java.lang.Long)
499          */
500         public void deleteGroup(final Long userId, final Long groupId) throws ObjectNotFoundException, InsufficientPermissionsException {
501                 // Validate.
502                 if (userId == null)
503                         throw new ObjectNotFoundException("No user specified");
504                 if (groupId == null)
505                         throw new ObjectNotFoundException("No group specified");
506
507                 // Do the actual work.
508                 final User owner = dao.getEntityById(User.class, userId);
509                 final Group group = dao.getEntityById(Group.class, groupId);
510                 // Only delete the group if actually owned by the user.
511                 if (group.getOwner().equals(owner)) {
512                         List<Folder> folders = dao.getFoldersPermittedForGroup(userId, groupId);
513                         for (Folder f : folders){
514                                 f.getPermissions().removeAll(group.getPermissions());
515                                 for(FileHeader file : f.getFiles())
516                                         file.getPermissions().removeAll(group.getPermissions());
517                         }
518                         List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
519                         for(FileHeader h : files)
520                                 h.getPermissions().removeAll(group.getPermissions());
521                         owner.removeSpecifiedGroup(group);
522                         dao.delete(group);
523                 }
524                 else throw new InsufficientPermissionsException("You are not the owner of this group");
525         }
526
527         @Override
528         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, InputStream stream)
529                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
530                         InsufficientPermissionsException, QuotaExceededException {
531                 File file = null;
532                 try {
533                         file = uploadFile(stream, userId);
534                 } catch ( IOException ioe) {
535                         // Supply a more accurate problem description.
536                         throw new GSSIOException("Problem creating file",ioe);
537                 }
538                 return createFile(userId, folderId, name, mimeType, file.length(), file.getAbsolutePath());
539         }
540
541         /* (non-Javadoc)
542          * @see gr.ebs.gss.server.ejb.ExternalAPIRemote#indexFile(java.lang.Long, boolean)
543          */
544         public void indexFile(Long fileId, boolean delete) {
545                 Connection qConn = null;
546                 Session session = null;
547                 MessageProducer sender = null;
548                 try {
549                         Context jndiCtx = new InitialContext();
550                         ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
551                         Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
552                         qConn = factory.createConnection();
553                         session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
554                         sender = session.createProducer(queue);
555
556                         MapMessage map = session.createMapMessage();
557                         map.setObject("id", fileId);
558                         map.setBoolean("delete", delete);
559                         sender.send(map);
560                 }
561                 catch (NamingException e) {
562                         logger.error("Index was not updated: ", e);
563                 }
564                 catch (JMSException e) {
565                         logger.error("Index was not updated: ", e);
566                 }
567                 finally {
568                         try {
569                                 if (sender != null)
570                                         sender.close();
571                                 if (session != null)
572                                         session.close();
573                                 if (qConn != null)
574                                         qConn.close();
575                         }
576                         catch (JMSException e) {
577                                 logger.warn(e);
578                         }
579                 }
580         }
581
582
583
584         /**
585          * A helper method that generates a unique file path for a stored file. The
586          * files are stored using random hash names that are distributed evenly in
587          * a 2-level tree of subdirectories named after the first two hex characters
588          * in the name. For example, file ab1234cd5769f will be stored in the path
589          * /file-repository-root/a/b/ab1234cd5769f. The directories will be created
590          * if they don't already exist.
591          *
592          * @return a unique new file path
593          */
594         private String generateRepositoryFilePath() {
595                 String filename = Long.toHexString(random.nextLong());
596                 String fileRepositoryPath = getConfiguration().getString("fileRepositoryPath","/tmp");
597                 File root = new File(fileRepositoryPath);
598                 if (!root.exists())
599                         root.mkdirs();
600                 File firstFolder = new File(root + File.separator + filename.substring(0, 1));
601                 if (!firstFolder.exists())
602                         firstFolder.mkdir();
603                 File secondFolder = new File(firstFolder + File.separator + filename.substring(1, 2));
604                 if (!secondFolder.exists())
605                         secondFolder.mkdir();
606                 return secondFolder + File.separator + filename;
607         }
608
609         /*
610          * (non-Javadoc)
611          *
612          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFile(java.lang.Long,
613          *      java.lang.Long)
614          */
615         public void deleteFile(final Long userId, final Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
616                 // Validate.
617                 if (userId == null)
618                         throw new ObjectNotFoundException("No user specified");
619                 if (fileId == null)
620                         throw new ObjectNotFoundException("No file specified");
621
622                 // Do the actual work.
623                 final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
624                 final Folder parent = file.getFolder();
625                 if (parent == null)
626                         throw new ObjectNotFoundException("The specified file has no parent folder");
627                 final User user = dao.getEntityById(User.class, userId);
628                 if (!file.hasDeletePermission(user))
629                         throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
630                 for (final FileBody body : file.getBodies())
631                         deleteActualFile(body.getStoredFilePath());
632                 dao.delete(file);
633                 touchParentFolders(parent, user, new Date());
634                 indexFile(fileId, true);
635         }
636
637         private void deleteActualFile(String filePath) {
638                 if (filePath == null)
639                         return;
640                 File file = new File(filePath);
641                 if (!file.delete())
642                         logger.error("Could not delete file " + filePath);
643         }
644
645         public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
646                 if (userId == null)
647                         throw new ObjectNotFoundException("No user specified");
648                 if (fileHeaderId == null)
649                         throw new ObjectNotFoundException("No file specified");
650                 if (StringUtils.isEmpty(tag))
651                         throw new ObjectNotFoundException("Tag is empty");
652
653                 final User user = dao.getEntityById(User.class, userId);
654                 final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId);
655                 final Folder parent = fh.getFolder();
656                 if (parent == null)
657                         throw new ObjectNotFoundException("The specified file has no parent folder");
658                 user.addTag(fh, tag);
659                 touchParentFolders(parent, user, new Date());
660         }
661
662         public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
663                 return dao.getUserTags(userId);
664         }
665
666         public void updateFile(Long userId, Long fileId, String name,
667                                 String tagSet, Date modificationDate, Boolean versioned,
668                                 Boolean readForAll,     Set<PermissionDTO> permissions)
669                         throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
670                 if (userId == null)
671                         throw new ObjectNotFoundException("No user specified");
672                 if (fileId == null)
673                         throw new ObjectNotFoundException("No file specified");
674                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
675                 final Folder parent = file.getFolder();
676                 if (parent == null)
677                         throw new ObjectNotFoundException("The specified file has no parent folder");
678
679                 User user = dao.getEntityById(User.class, userId);
680                 // Check permissions for modifying the file metadata.
681                 if ((name != null || tagSet != null || modificationDate != null || versioned != null) && !file.hasWritePermission(user))
682                         throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update file " + file.getName() + "(" + file.getId() + ")");
683                 // Check permissions for making file public.
684                 if (readForAll != null && !user.equals(file.getOwner()))
685                                 throw new InsufficientPermissionsException("Only the owner can make a file public or not public");
686                 // Check permissions for modifying the ACL.
687                 if(permissions != null && !permissions.isEmpty() &&     !file.hasModifyACLPermission(user))
688                         throw new InsufficientPermissionsException("User " + user.getId() +     " cannot update the permissions on file " +     file.getName() + "(" + file.getId() + ")");
689
690                 if (name != null) {
691                         // Do plain check for file already exists.
692                         // Extreme concurrency case should be caught by constraint violation later.
693                         if (dao.existsFolderOrFile(parent.getId(), name)) throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
694                         file.setName(name);
695                 }
696
697                 if (modificationDate != null)
698                         file.getAuditInfo().setModificationDate(modificationDate);
699                 else
700                         file.getAuditInfo().setModificationDate(new Date());
701                 file.getAuditInfo().setModifiedBy(user);
702
703                 List<FileTag> tags = file.getFileTags();
704                 if (tagSet != null) {
705                         Iterator<FileTag> i = tags.iterator();
706                         while (i.hasNext()) {
707                                 FileTag tag = i.next();
708                                 i.remove();
709                                 tag.setFile(null);
710                                 user.removeTag(tag);
711                                 dao.delete(tag);
712                         }
713                         dao.flush();
714                         StringTokenizer st = new StringTokenizer(tagSet, ",");
715                         while (st.hasMoreTokens())
716                                 new FileTag(user, file, st.nextToken().trim());
717                 }
718                 if (versioned != null && !file.isVersioned() == versioned) {
719                         if (file.isVersioned())
720                                 removeOldVersions(userId, fileId);
721                         file.setVersioned(versioned);
722                 }
723                 if (readForAll != null && user.equals(file.getOwner()))
724                         file.setReadForAll(readForAll);
725                 if (permissions != null && !permissions.isEmpty())
726                         setFilePermissions(file, permissions);
727
728                 /*
729                  * Force constraint violation to manifest itself here.
730                  * This should cover extreme concurrency cases that the simple check
731                  * above hasn't caught.
732                  */
733                 try {
734                         dao.flush();
735                 }
736                 catch (EJBTransactionRolledbackException e) {
737                         Throwable cause = e.getCause();
738                         if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
739                                 throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
740                         throw e;
741                 }
742
743                 touchParentFolders(parent, user, new Date());
744
745                 // Re-index the file if it was modified.
746                 if (name != null || tagSet != null)
747                         indexFile(fileId, false);
748         }
749
750         @Override
751         public InputStream getFileContents(Long userId, Long fileId)
752                         throws ObjectNotFoundException, InsufficientPermissionsException {
753                 if (userId == null)
754                         throw new ObjectNotFoundException("No user specified");
755                 if (fileId == null)
756                         throw new ObjectNotFoundException("No file specified");
757
758                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
759                 User user = dao.getEntityById(User.class, userId);
760                 if (!header.hasReadPermission(user)) {
761                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
762                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
763                 }
764
765                 File f = new File(header.getCurrentBody().getStoredFilePath());
766                 try {
767                         return new FileInputStream(f);
768                 } catch (FileNotFoundException e) {
769                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
770                         throw new ObjectNotFoundException("The file contents could not be located");
771                 }
772         }
773
774         /* (non-Javadoc)
775          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long)
776          */
777         public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
778                 if (userId == null)
779                         throw new ObjectNotFoundException("No user specified");
780                 if (fileId == null)
781                         throw new ObjectNotFoundException("No file specified");
782                 if (bodyId == null)
783                         throw new ObjectNotFoundException("No file specified");
784
785                 final FileHeader header = dao.getEntityById(FileHeader.class, fileId);
786                 final FileBody body = dao.getEntityById(FileBody.class, bodyId);
787                 final User user = dao.getEntityById(User.class, userId);
788                 if (!header.hasReadPermission(user)) {
789                         logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
790                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
791                 }
792
793                 File f = new File(body.getStoredFilePath());
794                 try {
795                         return new FileInputStream(f);
796                 } catch (FileNotFoundException e) {
797                         logger.error("Could not locate the contents of file " + f.getAbsolutePath());
798                         throw new ObjectNotFoundException("The file contents could not be located");
799                 }
800         }
801
802         /* (non-Javadoc)
803          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFile(java.lang.Long, java.lang.Long)
804          */
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                 dao.create(user);
1433                 // Make sure we get an ID in the user object.
1434                 dao.flush();
1435                 // Create the root folder for the user.
1436                 createFolder(user.getName(), null, user);
1437                 return user;
1438         }
1439
1440         @Override
1441         public User findUserByEmail(String email) {
1442                 return dao.findUserByEmail(email);
1443         }
1444
1445         @Override
1446         public void updateUser(User user) {
1447                 dao.update(user);
1448         }
1449
1450         @Override
1451         public User findUser(String username) {
1452                 if (username == null)
1453                         return null;
1454                 return dao.findUser(username);
1455         }
1456
1457         @Override
1458         public User updateUserToken(Long userId) throws ObjectNotFoundException {
1459                 if (userId == null)
1460                         throw new ObjectNotFoundException("No user specified");
1461                 User user = dao.getEntityById(User.class, userId);
1462                 user.generateAuthToken();
1463                 return user;
1464         }
1465
1466         /* (non-Javadoc)
1467          * @see gr.ebs.gss.server.ejb.ExternalAPI#getFolderPermissions(java.lang.Long, java.lang.Long)
1468          */
1469         @Override
1470         public Set<PermissionDTO> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1471                 if (userId == null)
1472                         throw new ObjectNotFoundException("No user specified");
1473                 if (folderId == null)
1474                         throw new ObjectNotFoundException("No folder specified");
1475                 User user = dao.getEntityById(User.class, userId);
1476                 Folder folder = dao.getEntityById(Folder.class, folderId);
1477                 if(!folder.hasReadPermission(user))
1478                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1479                 Set<Permission> perms = folder.getPermissions();
1480                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1481                 for (Permission perm : perms)
1482                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1483                                 result.add(perm.getDTO());
1484                 for (Permission perm : perms)
1485                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1486                         } else
1487                                 result.add(perm.getDTO());
1488                 return result;
1489
1490         }
1491
1492         /**
1493          * Set the provided permissions as the new permissions of the specified
1494          * folder.
1495          *
1496          * @param user
1497          * @param folder
1498          * @param permissions
1499          * @throws ObjectNotFoundException
1500          * @throws InsufficientPermissionsException
1501          */
1502         private void setFolderPermissions(User user, Folder folder, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1503                 // Delete previous entries
1504                 for (Permission perm: folder.getPermissions())
1505                         dao.delete(perm);
1506                 folder.getPermissions().clear();
1507                 for (PermissionDTO dto : permissions) {
1508                         if (dto.getUser()!=null && dto.getUser().getId().equals(folder.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
1509                                         throw new InsufficientPermissionsException("Can't remove permissions from owner");
1510                         // Don't include 'empty' permission
1511                         if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1512                         folder.addPermission(getPermission(dto));
1513                 }
1514                 dao.update(folder);
1515                 for (FileHeader file : folder.getFiles()) {
1516                         setFilePermissions(file, permissions);
1517                         Date now = new Date();
1518                         file.getAuditInfo().setModificationDate(now);
1519                         file.getAuditInfo().setModifiedBy(user);
1520                 }
1521                 for (Folder sub : folder.getSubfolders())
1522                         setFolderPermissions(user, sub, permissions);
1523         }
1524
1525         private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
1526                 Permission res = new Permission();
1527                 if (dto.getGroup() != null)
1528                         res.setGroup(dao.getEntityById(Group.class, dto.getGroup().getId()));
1529                 else if (dto.getUser() != null)
1530                         if (dto.getUser().getId() == null)
1531                                 res.setUser(dao.getUser(dto.getUser().getUsername()));
1532                         else
1533                                 res.setUser(dao.getEntityById(User.class, dto.getUser().getId()));
1534                 res.setRead(dto.hasRead());
1535                 res.setWrite(dto.hasWrite());
1536                 res.setModifyACL(dto.hasModifyACL());
1537                 return res;
1538         }
1539
1540         /* (non-Javadoc)
1541          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
1542          */
1543         @Override
1544         public List<UserDTO> getUsersByUserNameLike(String username) {
1545                 List<User> users = dao.getUsersByUserNameLike(username);
1546                 List<UserDTO> result = new ArrayList<UserDTO>();
1547                 for (User u : users)
1548                         result.add(u.getDTO());
1549                 return result;
1550
1551         }
1552
1553         /* (non-Javadoc)
1554          * @see gr.ebs.gss.server.ejb.ExternalAPI#addUserToGroup(java.lang.Long, java.lang.Long, java.lang.Long)
1555          */
1556         @Override
1557         public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1558                 if (userId == null)
1559                         throw new ObjectNotFoundException("No user specified");
1560                 if (groupId == null)
1561                         throw new ObjectNotFoundException("No group specified");
1562                 if (userToAddId == null)
1563                         throw new ObjectNotFoundException("No user to add specified");
1564                 User user = dao.getEntityById(User.class, userId);
1565                 Group group = dao.getEntityById(Group.class, groupId);
1566                 if (!group.getOwner().equals(user))
1567                         throw new InsufficientPermissionsException();
1568                 User userToAdd = dao.getEntityById(User.class, userToAddId);
1569                 if (group.contains(userToAdd))
1570                         throw new DuplicateNameException("User already exists in group");
1571                 group.getMembers().add(userToAdd);
1572                 dao.update(group);
1573
1574         }
1575
1576         @Override
1577         public void invalidateUserToken(Long userId) throws ObjectNotFoundException {
1578                 if (userId == null)
1579                         throw new ObjectNotFoundException("No user specified");
1580                 User user = dao.getEntityById(User.class, userId);
1581                 user.invalidateAuthToken();
1582                 return;
1583         }
1584
1585         @Override
1586         public List<FolderDTO> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
1587                 if (userId == null)
1588                         throw new ObjectNotFoundException("No user specified");
1589                 List<Folder> folders = dao.getSharedRootFolders(userId);
1590                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1591                 for (Folder f : folders) {
1592                         FolderDTO dto = f.getDTO();
1593                         dto.setSubfolders(getSharedSubfolders(userId, f.getId()));
1594                         result.add(dto);
1595                 }
1596                 return result;
1597         }
1598
1599         /* (non-Javadoc)
1600          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeMemberFromGroup(java.lang.Long, java.lang.Long, java.lang.Long)
1601          */
1602         @Override
1603         public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
1604                 if (userId == null)
1605                         throw new ObjectNotFoundException("No user specified");
1606                 if (groupId == null)
1607                         throw new ObjectNotFoundException("No group specified");
1608                 if (memberId == null)
1609                         throw new ObjectNotFoundException("No member specified");
1610                 User owner = dao.getEntityById(User.class, userId);
1611                 Group group = dao.getEntityById(Group.class, groupId);
1612                 User member = dao.getEntityById(User.class, memberId);
1613                 if (!group.getOwner().equals(owner))
1614                         throw new InsufficientPermissionsException("User is not the owner of the group");
1615                 group.removeMemberFromGroup(member);
1616                 dao.update(group);
1617
1618         }
1619
1620         @Override
1621         public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
1622                 List<User> users = dao.getUsersSharingFoldersForUser(userId);
1623                 List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
1624                 List<UserDTO> res = new ArrayList<UserDTO>();
1625                 for (User u : users)
1626                         res.add(u.getDTO());
1627                 for(User fu : usersFiles)
1628                         if(!users.contains(fu))
1629                                 res.add(fu.getDTO());
1630                 return res;
1631         }
1632
1633         @Override
1634         public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1635                 if (userId == null)
1636                         throw new ObjectNotFoundException("No user specified");
1637                 if (fileId == null)
1638                         throw new ObjectNotFoundException("No folder specified");
1639                 User user = dao.getEntityById(User.class, userId);
1640                 FileHeader folder = dao.getEntityById(FileHeader.class, fileId);
1641                 if(!folder.hasReadPermission(user))
1642                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1643                 Set<Permission> perms = folder.getPermissions();
1644                 Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1645                 for (Permission perm : perms)
1646                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1647                                 result.add(perm.getDTO());
1648                 for (Permission perm : perms)
1649                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1650                         } else
1651                                 result.add(perm.getDTO());
1652                 return result;
1653         }
1654
1655         /**
1656          * Set the provided permissions as the new permissions of the specified
1657          * file. This method sets the modification date/user attributes to the
1658          * current values as a side effect.
1659          *
1660          * @param file
1661          * @param permissions
1662          * @throws ObjectNotFoundException
1663          * @throws InsufficientPermissionsException
1664          */
1665         private void setFilePermissions(FileHeader file,
1666                                 Set<PermissionDTO> permissions)
1667                         throws ObjectNotFoundException, InsufficientPermissionsException {
1668                 if (permissions != null && !permissions.isEmpty()) {
1669                         // Delete previous entries.
1670                         for (Permission perm: file.getPermissions())
1671                                 dao.delete(perm);
1672                         file.getPermissions().clear();
1673                         for (PermissionDTO dto : permissions) {
1674                                 if (dto.getUser()!=null && dto.getUser().getId().equals(file.getOwner().getId()) && (!dto.hasRead() || !dto.hasWrite() || !dto.hasModifyACL()))
1675                                         throw new InsufficientPermissionsException("Can't remove permissions from owner");
1676                                 // Don't include 'empty' permission.
1677                                 if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1678                                 file.addPermission(getPermission(dto));
1679                         }
1680                         dao.flush();
1681                 }
1682         }
1683
1684         @Override
1685         public List<FileHeaderDTO> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1686                 if (userId == null)
1687                         throw new ObjectNotFoundException("No user specified");
1688                 List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1689                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1690                 for (FileHeader f : files)
1691                         result.add(f.getDTO());
1692                 return result;
1693         }
1694
1695         @Override
1696         public List<FileHeaderDTO> getSharedFiles(Long userId) throws ObjectNotFoundException {
1697                 if (userId == null)
1698                         throw new ObjectNotFoundException("No user specified");
1699                 List<FileHeader> files = dao.getSharedFiles(userId);
1700                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1701                 for (FileHeader f : files)
1702                         result.add(f.getDTO());
1703                 return result;
1704         }
1705
1706         @Override
1707         public List<FolderDTO> getSharedFolders(Long userId) throws ObjectNotFoundException {
1708                 if (userId == null)
1709                         throw new ObjectNotFoundException("No user specified");
1710                 List<Folder> folders = dao.getSharedFolders(userId);
1711                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1712                 for (Folder f : folders)
1713                         result.add(f.getDTO());
1714                 return result;
1715         }
1716
1717         /* (non-Javadoc)
1718          * @see gr.ebs.gss.server.ejb.ExternalAPI#getSharedFiles(java.lang.Long, java.lang.Long)
1719          */
1720         @Override
1721         public List<FileHeaderDTO> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1722                 if (ownerId == null)
1723                         throw new ObjectNotFoundException("No owner specified");
1724                 if (callingUserId == null)
1725                         throw new ObjectNotFoundException("No calling user specified");
1726                 List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1727                 List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1728                 for (FileHeader f : folders)
1729                         result.add(f.getDTO());
1730                 return result;
1731         }
1732
1733         @Override
1734         public List<FolderDTO> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1735                 if (ownerId == null)
1736                         throw new ObjectNotFoundException("No owner specified");
1737                 if (callingUserId == null)
1738                         throw new ObjectNotFoundException("No calling user specified");
1739                 List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
1740                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1741                 for (Folder f : folders) {
1742                         FolderDTO dto = f.getDTO();
1743                         dto.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
1744                         result.add(dto);
1745                 }
1746                 return result;
1747
1748         }
1749
1750         @Override
1751         public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1752                 if (userId == null)
1753                         throw new ObjectNotFoundException("No user specified");
1754                 if (folderId == null)
1755                         throw new ObjectNotFoundException("No folder specified");
1756                 User user = dao.getEntityById(User.class, userId);
1757                 Folder folder = dao.getEntityById(Folder.class, folderId);
1758                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1759                 if (folder.isShared(user))
1760                         for (Folder f : folder.getSubfolders())
1761                                 if (f.isShared(user) && !f.isDeleted())
1762                                         result.add(f.getDTO());
1763                 return result;
1764         }
1765
1766         @Override
1767         public List<FolderDTO> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
1768                 if (userId == null)
1769                         throw new ObjectNotFoundException("No user specified");
1770                 if (callingUserId == null)
1771                         throw new ObjectNotFoundException("No user specified");
1772                 if (folderId == null)
1773                         throw new ObjectNotFoundException("No folder specified");
1774                 User user = dao.getEntityById(User.class, callingUserId);
1775                 Folder folder = dao.getEntityById(Folder.class, folderId);
1776                 List<FolderDTO> result = new ArrayList<FolderDTO>();
1777                 if (folder.isSharedForOtherUser(user))
1778                         for (Folder f : folder.getSubfolders())
1779                                 if (f.isSharedForOtherUser(user) && !f.isDeleted()){
1780                                         FolderDTO dto = f.getDTO();
1781                                         dto.setSubfolders(getSharedSubfolders(userId, callingUserId, dto.getId()));
1782                                         result.add(dto);
1783                                 }
1784                 return result;
1785
1786         }
1787
1788         /* (non-Javadoc)
1789          * @see gr.ebs.gss.server.ejb.ExternalAPI#searchFiles(java.lang.Long, java.lang.String)
1790          */
1791         @Override
1792         public List<FileHeaderDTO> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1793                 if (userId == null)
1794                         throw new ObjectNotFoundException("No user specified");
1795                 User user = getUser(userId);
1796                 if (query == null)
1797                         throw new ObjectNotFoundException("No query specified");
1798                 List<FileHeader> files = search(user.getId(), query);
1799                 List<FileHeaderDTO> res = new ArrayList<FileHeaderDTO>();
1800                 for(FileHeader f : files)
1801                         res.add(f.getDTO());
1802                 return res;
1803         }
1804
1805         /**
1806          * Performs the actuals search on the solr server and returns the results
1807          *
1808          * We have to use the dismax query type (instead of the
1809          * standard) because it allows for search time field boosting. This is because we can't use indexing
1810          * time field boosting due to the patched rich indexing API that does not allow it
1811          *
1812          * @param userId
1813          * @param query
1814          * @return a List of FileHeader objects
1815          */
1816         private List<FileHeader> search(Long userId, String query) {
1817                 try {
1818                         HttpClient httpClient = new HttpClient();
1819
1820                         GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl"));
1821                         NameValuePair[] params = {new NameValuePair("qt", "dismax"),
1822                                                                                 new NameValuePair("q", query),
1823                                                                                 new NameValuePair("sort", "score desc"),
1824                                                                                 new NameValuePair("indent", "on")};
1825                         method.setQueryString(params);
1826                         int retryCount = 0;
1827                         int statusCode = 0;
1828                         String response = null;
1829                         do {
1830                                 statusCode = httpClient.executeMethod(method);
1831                                 logger.debug("HTTP status: " + statusCode);
1832                                 response = method.getResponseBodyAsString();
1833                                 logger.debug(response);
1834                                 retryCount++;
1835                                 if (statusCode != 200 && retryCount < 3)
1836                                         try {
1837                                                 Thread.sleep(3000); //Give Solr a little time to be available
1838                                         } catch (InterruptedException e) {
1839                                         }
1840                         } while (statusCode != 200 && retryCount < 3);
1841                         if (statusCode != 200)
1842                                 throw new EJBException("Search query return error:\n" + response);
1843
1844                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1845                         DocumentBuilder db = dbf.newDocumentBuilder();
1846                         Document doc = db.parse(method.getResponseBodyAsStream());
1847                         method.releaseConnection();
1848
1849                         Node root = doc.getElementsByTagName("response").item(0);
1850                         Node lst = root.getFirstChild().getNextSibling();
1851                         Node status = lst.getFirstChild().getNextSibling();
1852                         if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") &&
1853                                 status.getTextContent().equals("0")) {
1854                                 List<FileHeader> fileResult = new ArrayList<FileHeader>();
1855                                 Node result = lst.getNextSibling().getNextSibling();
1856                                 NodeList docs = result.getChildNodes();
1857                                 User user = getUser(userId);
1858                                 for (int i=1; i<docs.getLength(); i=i+2) {
1859                                         Node d = docs.item(i);
1860                                         NodeList docData = d.getChildNodes();
1861                                         for (int j=1; j<docData.getLength(); j=j+2) {
1862                                                 Node dd = docData.item(j);
1863                                                 if (dd.getAttributes().item(0).getNodeName().equals("name") &&
1864                                                         dd.getAttributes().item(0).getNodeValue().equals("id")) {
1865                                                         Long fileId = Long.valueOf(dd.getTextContent());
1866                                                         try {
1867                                                                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1868                                                                 if (file.hasReadPermission(user)) {
1869                                                                         fileResult.add(file);
1870                                                                         logger.debug("File added " + fileId);
1871                                                                 }
1872                                                         } catch (ObjectNotFoundException e) {
1873                                                                 logger.warn("Search result not found", e);
1874                                                         }
1875                                                 }
1876                                         }
1877                                 }
1878                                 return fileResult;
1879                         }
1880                         throw new EJBException();
1881                 } catch (HttpException e) {
1882                         throw new EJBException(e);
1883                 } catch (IOException e) {
1884                         throw new EJBException(e);
1885                 } catch (SAXException e) {
1886                         throw new EJBException(e);
1887                 } catch (ParserConfigurationException e) {
1888                         throw new EJBException(e);
1889                 } catch (ObjectNotFoundException e) {
1890                         throw new EJBException(e);
1891                 }
1892         }
1893
1894         /* (non-Javadoc)
1895          * @see gr.ebs.gss.server.ejb.ExternalAPI#copyFiles(java.lang.Long, java.util.List, java.lang.Long)
1896          */
1897         @Override
1898         public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1899                 for(Long l : fileIds){
1900                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1901                         copyFile(userId, l, destId, file.getName());
1902                 }
1903
1904
1905         }
1906
1907         /* (non-Javadoc)
1908          * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFiles(java.lang.Long, java.util.List, java.lang.Long)
1909          */
1910         @Override
1911         public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1912                 for(Long l : fileIds){
1913                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1914                         moveFile(userId, l, destId, file.getName());
1915                 }
1916
1917         }
1918
1919         /* (non-Javadoc)
1920          * @see gr.ebs.gss.server.ejb.ExternalAPI#deleteFiles(java.lang.Long, java.util.List)
1921          */
1922         @Override
1923         public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1924                 if (userId == null)
1925                         throw new ObjectNotFoundException("No user specified");
1926                 final User user = dao.getEntityById(User.class, userId);
1927                 List<String> filesToRemove = new ArrayList<String>();
1928                 //first delete database objects
1929                 for(Long fileId : fileIds){
1930                         if (fileId == null)
1931                                 throw new ObjectNotFoundException("No file specified");
1932                         final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1933                         final Folder parent = file.getFolder();
1934                         if (parent == null)
1935                                 throw new ObjectNotFoundException("The specified file has no parent folder");
1936                         if (!file.hasDeletePermission(user))
1937                                 throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1938
1939                         parent.removeFile(file);
1940                         for (final FileBody body : file.getBodies())
1941                                 filesToRemove.add(body.getStoredFilePath());
1942                         dao.delete(file);
1943                         touchParentFolders(parent, user, new Date());
1944                 }
1945                 //then remove physical files if everything is ok
1946                 for(String physicalFileName : filesToRemove)
1947                         deleteActualFile(physicalFileName);
1948                 //then unindex deleted files
1949                 for(Long fileId : fileIds)
1950                         indexFile(fileId, true);
1951
1952         }
1953
1954         /* (non-Javadoc)
1955          * @see gr.ebs.gss.server.ejb.ExternalAPI#moveFilesToTrash(java.lang.Long, java.util.List)
1956          */
1957         @Override
1958         public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1959                 for(Long l : fileIds)
1960                         moveFileToTrash(userId, l);
1961
1962         }
1963
1964         @Override
1965         public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1966                 for(Long l : fileIds)
1967                         removeFileFromTrash(userId, l);
1968
1969         }
1970
1971         @Override
1972         public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1973                 if (userId == null)
1974                         throw new ObjectNotFoundException("No user specified");
1975                 User user = dao.getEntityById(User.class, userId);
1976                 Nonce nonce = Nonce.createNonce(user.getId());
1977                 dao.create(nonce);
1978                 return nonce;
1979         }
1980
1981         @Override
1982         public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1983                 if (userId == null)
1984                         throw new ObjectNotFoundException("No user specified");
1985                 if (nonce == null)
1986                         throw new ObjectNotFoundException("No nonce specified");
1987                 return dao.getNonce(nonce, userId);
1988         }
1989
1990         @Override
1991         public void removeNonce(Long id) throws ObjectNotFoundException {
1992                 if (id == null)
1993                         throw new ObjectNotFoundException("No nonce specified");
1994                 Nonce nonce = dao.getEntityById(Nonce.class, id);
1995                 dao.delete(nonce);
1996         }
1997
1998         @Override
1999         public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
2000                 if (userId == null)
2001                         throw new ObjectNotFoundException("No user specified");
2002                 User user = dao.getEntityById(User.class, userId);
2003                 user.setNonce(nonce);
2004                 user.setNonceExpiryDate(nonceExpiryDate);
2005         }
2006
2007         /* (non-Javadoc)
2008          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUserStatistics(java.lang.Long)
2009          */
2010         @Override
2011         public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
2012                 if (userId == null)
2013                         throw new ObjectNotFoundException("No user specified");
2014                 StatsDTO stats = new StatsDTO();
2015                 stats.setFileCount(dao.getFileCount(userId));
2016                 Long fileSize = dao.getFileSize(userId);
2017                 stats.setFileSize(fileSize);
2018                 Long quota = getQuota(userId);
2019                 Long quotaLeft = quota - fileSize;
2020                 stats.setQuotaLeftSize(quotaLeft);
2021                 return stats;
2022         }
2023
2024         /* (non-Javadoc)
2025          * @see gr.ebs.gss.server.ejb.ExternalAPI#getVersions(java.lang.Long, java.lang.Long)
2026          */
2027         @Override
2028         public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2029                 if (userId == null)
2030                         throw new ObjectNotFoundException("No user specified");
2031                 if (fileId == null)
2032                         throw new ObjectNotFoundException("No file specified");
2033                 User user = dao.getEntityById(User.class, userId);
2034                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2035                 if(!header.hasReadPermission(user))
2036                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2037                 List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
2038                 for(int i = header.getBodies().size()-1 ; i>=0; i--)
2039                         result.add(header.getBodies().get(i).getDTO());
2040                 return result;
2041         }
2042
2043         /* (non-Javadoc)
2044          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeVersion(java.lang.Long, java.lang.Long, java.lang.Long)
2045          */
2046         @Override
2047         public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
2048                 if (userId == null)
2049                         throw new ObjectNotFoundException("No user specified");
2050                 if (fileId == null)
2051                         throw new ObjectNotFoundException("No file specified");
2052                 if (bodyId == null)
2053                         throw new ObjectNotFoundException("No body specified");
2054                 User user = dao.getEntityById(User.class, userId);
2055                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2056                 if(!header.hasWritePermission(user))
2057                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2058                 FileBody body = dao.getEntityById(FileBody.class, bodyId);
2059                 if(body.equals(header.getCurrentBody())){
2060
2061                         if(header.getBodies().size() == 1)
2062                                 throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
2063                         for(FileBody b : header.getBodies())
2064                                 if(b.getVersion() == body.getVersion()-1)
2065                                         header.setCurrentBody(b);
2066                 }
2067                 deleteActualFile(body.getStoredFilePath());
2068                 header.getBodies().remove(body);
2069
2070                 Folder parent = header.getFolder();
2071                 touchParentFolders(parent, user, new Date());
2072
2073         }
2074
2075         @Override
2076         public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
2077                 if (userId == null)
2078                         throw new ObjectNotFoundException("No user specified");
2079                 if (fileId == null)
2080                         throw new ObjectNotFoundException("No file specified");
2081                 User user = dao.getEntityById(User.class, userId);
2082                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2083                 if(!header.hasWritePermission(user))
2084                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2085                 FileBody body = dao.getFileVersion(fileId, version);
2086                 final File fileContents = new File(body.getStoredFilePath());
2087
2088                 try {
2089                         updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2090                 } catch (FileNotFoundException e) {
2091                         throw new GSSIOException(e);
2092                 }
2093
2094         }
2095
2096         /* (non-Javadoc)
2097          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
2098          */
2099         @Override
2100         public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2101                 if (userId == null)
2102                         throw new ObjectNotFoundException("No user specified");
2103                 if (fileId == null)
2104                         throw new ObjectNotFoundException("No file specified");
2105                 User user = dao.getEntityById(User.class, userId);
2106                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2107                 if(!header.hasWritePermission(user))
2108                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2109                 Iterator<FileBody> it = header.getBodies().iterator();
2110                 while(it.hasNext()){
2111                         FileBody body = it.next();
2112                         if(!body.equals(header.getCurrentBody())){
2113                                 deleteActualFile(body.getStoredFilePath());
2114                                 it.remove();
2115                                 dao.delete(body);
2116                         }
2117                 }
2118                 header.getCurrentBody().setVersion(1);
2119
2120                 Folder parent = header.getFolder();
2121                 touchParentFolders(parent, user, new Date());
2122         }
2123
2124         /**
2125          * Gets the quota left for specified userId
2126          * @param userId
2127          * @return
2128          */
2129         private Long getQuotaLeft(Long userId){
2130                 Long fileSize = dao.getFileSize(userId);
2131                 Long quota = getQuota(userId);
2132                 return quota - fileSize;
2133         }
2134
2135         /**
2136          * Gets the quota for specified userId
2137          * @param userId
2138          * @return
2139          */
2140         private Long getQuota(@SuppressWarnings("unused") Long userId){
2141                 Long quota = getConfiguration().getLong("quota", new Long(52428800L));
2142                 return quota;
2143         }
2144
2145         public void rebuildSolrIndex() {
2146                 MessageProducer sender = null;
2147                 Session session = null;
2148                 Connection qConn = null;
2149                 try {
2150                         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2151                         DocumentBuilder db = dbf.newDocumentBuilder();
2152                         Document doc = db.newDocument();
2153                         Node root = doc.createElement("delete");
2154                         doc.appendChild(root);
2155                         Node queryNode = doc.createElement("query");
2156                         root.appendChild(queryNode);
2157                         queryNode.appendChild(doc.createTextNode("*:*"));
2158
2159                         TransformerFactory fact = TransformerFactory.newInstance();
2160                         Transformer trans = fact.newTransformer();
2161                         trans.setOutputProperty(OutputKeys.INDENT, "yes");
2162                         StringWriter sw = new StringWriter();
2163                         StreamResult sr = new StreamResult(sw);
2164                         DOMSource source = new DOMSource(doc);
2165                         trans.transform(source, sr);
2166                         logger.debug(sw.toString());
2167
2168                         HttpClient httpClient = new HttpClient();
2169                         PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2170                         method.setRequestEntity(new StringRequestEntity(sw.toString()));
2171                         int retryCount = 0;
2172                         int statusCode = 0;
2173                         String response = null;
2174                         do {
2175                                 statusCode = httpClient.executeMethod(method);
2176                                 logger.debug("HTTP status: " + statusCode);
2177                                 response = method.getResponseBodyAsString();
2178                                 logger.debug(response);
2179                                 retryCount++;
2180                                 if (statusCode != 200 && retryCount < 3)
2181                                         try {
2182                                                 Thread.sleep(10000); //Give Solr a little time to be available
2183                                         } catch (InterruptedException e) {
2184                                         }
2185                         } while (statusCode != 200 && retryCount < 3);
2186                         method.releaseConnection();
2187                         if (statusCode != 200)
2188                                 throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response);
2189                         List<Long> fileIds = dao.getAllFileIds();
2190
2191                         Context jndiCtx = new InitialContext();
2192                         ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
2193                         Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
2194                         qConn = factory.createConnection();
2195                         session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
2196                         sender = session.createProducer(queue);
2197
2198                         for (Long id : fileIds) {
2199                                 MapMessage map = session.createMapMessage();
2200                                 map.setObject("id", id);
2201                                 map.setBoolean("delete", false);
2202                                 sender.send(map);
2203                         }
2204                         sendOptimize(httpClient, 0);
2205                 } catch (DOMException e) {
2206                         throw new EJBException(e);
2207                 } catch (TransformerConfigurationException e) {
2208                         throw new EJBException(e);
2209                 } catch (IllegalArgumentException e) {
2210                         throw new EJBException(e);
2211                 } catch (HttpException e) {
2212                         throw new EJBException(e);
2213                 } catch (UnsupportedEncodingException e) {
2214                         throw new EJBException(e);
2215                 } catch (ParserConfigurationException e) {
2216                         throw new EJBException(e);
2217                 } catch (TransformerException e) {
2218                         throw new EJBException(e);
2219                 } catch (IOException e) {
2220                         throw new EJBException(e);
2221                 } catch (NamingException e) {
2222                         throw new EJBException(e);
2223                 } catch (JMSException e) {
2224                         throw new EJBException(e);
2225                 }
2226                 finally {
2227                         try {
2228                                 if (sender != null)
2229                                         sender.close();
2230                                 if (session != null)
2231                                         session.close();
2232                                 if (qConn != null)
2233                                         qConn.close();
2234                         }
2235                         catch (JMSException e) {
2236                                 logger.warn(e);
2237                         }
2238                 }
2239         }
2240
2241         /**
2242          * Sends a optimize message to the solr server
2243          *
2244          * @param httpClient
2245          * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive
2246          *                                      calls to stop the recursion
2247          * @throws UnsupportedEncodingException
2248          * @throws IOException
2249          * @throws HttpException
2250          */
2251         private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException {
2252                 PostMethod method = null;
2253                 try {
2254                         logger.debug("Optimize retry: " + retryCount);
2255                         method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2256                         method.setRequestEntity(new StringRequestEntity("<optimize/>", "text/xml", "iso8859-1"));
2257                         int statusCode = httpClient.executeMethod(method);
2258                         logger.debug("HTTP status: " + statusCode);
2259                         String response = method.getResponseBodyAsString();
2260                         logger.debug(response);
2261                         if (statusCode != 200 && retryCount < 2) {
2262                                 try {
2263                                         Thread.sleep(10000); //Give Solr a little time to be available
2264                                 } catch (InterruptedException e) {
2265                                 }
2266                                 sendOptimize(httpClient, retryCount + 1);
2267                         }
2268                 }
2269                 finally {
2270                         if (method != null)
2271                                 method.releaseConnection();
2272                 }
2273         }
2274
2275         public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2276                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2277                         InsufficientPermissionsException, QuotaExceededException {
2278                 // Validate.
2279                 if (userId == null)
2280                         throw new ObjectNotFoundException("No user specified");
2281                 if (folderId == null)
2282                         throw new ObjectNotFoundException("No folder specified");
2283                 String contentType = mimeType;
2284                 if (StringUtils.isEmpty(mimeType))
2285                         contentType = DEFAULT_MIME_TYPE;
2286                 if (StringUtils.isEmpty(name))
2287                         throw new ObjectNotFoundException("No file name specified");
2288                 if (dao.existsFolderOrFile(folderId, name))
2289                         throw new DuplicateNameException("A folder or file with the name '" + name +
2290                                                 "' already exists at this level");
2291
2292                 // Do the actual work.
2293                 Folder parent = null;
2294                 try {
2295                         parent = dao.getEntityById(Folder.class, folderId);
2296                 } catch (final ObjectNotFoundException onfe) {
2297                         // Supply a more accurate problem description.
2298                         throw new ObjectNotFoundException("Parent folder not found");
2299                 }
2300                 final User owner = dao.getEntityById(User.class, userId);
2301                 if (!parent.hasWritePermission(owner))
2302                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2303                 final FileHeader file = new FileHeader();
2304                 file.setName(name);
2305                 parent.addFile(file);
2306                 // set file owner to folder owner
2307                 file.setOwner(parent.getOwner());
2308
2309                 final Date now = new Date();
2310                 final AuditInfo auditInfo = new AuditInfo();
2311                 auditInfo.setCreatedBy(owner);
2312                 auditInfo.setCreationDate(now);
2313                 auditInfo.setModifiedBy(owner);
2314                 auditInfo.setModificationDate(now);
2315                 file.setAuditInfo(auditInfo);
2316                 // TODO set the proper versioning flag on creation
2317                 file.setVersioned(false);
2318
2319                 for (final Permission p : parent.getPermissions()) {
2320                         final Permission permission = new Permission();
2321                         permission.setGroup(p.getGroup());
2322                         permission.setUser(p.getUser());
2323                         permission.setRead(p.getRead());
2324                         permission.setWrite(p.getWrite());
2325                         permission.setModifyACL(p.getModifyACL());
2326                         file.addPermission(permission);
2327                 }
2328
2329                 // Create the file body.
2330                 try {
2331                         createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2332                 } catch (FileNotFoundException e) {
2333                         throw new GSSIOException(e);
2334                 }
2335                 touchParentFolders(parent, owner, new Date());
2336                 dao.flush();
2337                 indexFile(file.getId(), false);
2338
2339                 return file.getDTO();
2340         }
2341
2342         /* (non-Javadoc)
2343          * @see gr.ebs.gss.server.ejb.ExternalAPI#updateFileContents(java.lang.Long, java.lang.Long, java.lang.String, java.io.InputStream)
2344          */
2345         public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2346                 if (userId == null)
2347                         throw new ObjectNotFoundException("No user specified");
2348                 if (fileId == null)
2349                         throw new ObjectNotFoundException("No file specified");
2350                 String contentType = mimeType;
2351
2352                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2353
2354                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2355                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2356                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2357                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2358                         contentType = identifyMimeType(file.getName());
2359
2360                 final User owner = dao.getEntityById(User.class, userId);
2361                 if (!file.hasWritePermission(owner))
2362                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2363                 final Date now = new Date();
2364                 final AuditInfo auditInfo = new AuditInfo();
2365                 auditInfo.setCreatedBy(owner);
2366                 auditInfo.setCreationDate(now);
2367                 auditInfo.setModifiedBy(owner);
2368                 auditInfo.setModificationDate(now);
2369                 try {
2370                         createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2371                 } catch (FileNotFoundException e) {
2372                         throw new GSSIOException(e);
2373                 }
2374                 Folder parent = file.getFolder();
2375                 touchParentFolders(parent, owner, new Date());
2376
2377                 indexFile(fileId, false);
2378                 return file.getDTO();
2379         }
2380
2381         /**
2382          * Helper method for identifying mime type by examining the filename extension
2383          *
2384          * @param filename
2385          * @return the mime type
2386          */
2387         private String identifyMimeType(String filename) {
2388                 if (filename.indexOf('.') != -1) {
2389                         String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2390                         if (".doc".equals(extension))
2391                                 return "application/msword";
2392                         else if (".xls".equals(extension))
2393                                 return "application/vnd.ms-excel";
2394                         else if (".ppt".equals(extension))
2395                                 return "application/vnd.ms-powerpoint";
2396                         else if (".pdf".equals(extension))
2397                                 return "application/pdf";
2398                         else if (".gif".equals(extension))
2399                                 return "image/gif";
2400                         else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2401                                 return "image/jpeg";
2402                         else if (".tiff".equals(extension) || ".tif".equals(extension))
2403                                 return "image/tiff";
2404                         else if (".png".equals(extension))
2405                                 return "image/png";
2406                         else if (".bmp".equals(extension))
2407                                 return "image/bmp";
2408                 }
2409                 // when all else fails assign the default mime type
2410                 return DEFAULT_MIME_TYPE;
2411         }
2412
2413         /**
2414          * Helper method to create a new file body and attach it as the current body
2415          * of the provided file header.
2416          *
2417          * @param name the original file name
2418          * @param mimeType the content type
2419          * @param fileSize the uploaded file size
2420          * @param filePath the uploaded file full path
2421          * @param header the file header that will be associated with the new body
2422          * @param auditInfo the audit info
2423          * @param owner the owner of the file
2424          * @throws FileNotFoundException
2425          * @throws QuotaExceededException
2426          */
2427         private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2428                                 FileHeader header, AuditInfo auditInfo)
2429                         throws FileNotFoundException, QuotaExceededException {
2430
2431                 long currentTotalSize = 0;
2432                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2433                         currentTotalSize = header.getTotalSize();
2434                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2435                 if(quotaLeft < fileSize-currentTotalSize) {
2436                         // quota exceeded -> delete the file
2437                         deleteActualFile(filePath);
2438                         throw new QuotaExceededException("Not enough free space available");
2439                 }
2440
2441                 FileBody body = new FileBody();
2442
2443                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2444                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2445                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2446                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2447                         body.setMimeType(identifyMimeType(name));
2448                 else
2449                         body.setMimeType(mimeType);
2450                 body.setAuditInfo(auditInfo);
2451                 body.setFileSize(fileSize);
2452                 body.setOriginalFilename(name);
2453                 body.setStoredFilePath(filePath);
2454                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2455                 if(!header.isVersioned() && header.getCurrentBody() != null){
2456                         header.setCurrentBody(null);
2457                         if (header.getBodies() != null) {
2458                                 Iterator<FileBody> it = header.getBodies().iterator();
2459                                 while(it.hasNext()){
2460                                         FileBody bo = it.next();
2461                                         deleteActualFile(bo.getStoredFilePath());
2462                                         it.remove();
2463                                         dao.delete(bo);
2464                                 }
2465                         }
2466                 }
2467
2468                 dao.flush();
2469                 header.addBody(body);
2470                 header.setAuditInfo(auditInfo);
2471
2472                 dao.create(body);
2473         }
2474
2475
2476         @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2477         public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2478                 if (userId == null)
2479                         throw new ObjectNotFoundException("No user specified");
2480                 User owner = dao.getEntityById(User.class, userId);
2481                 if(owner == null)
2482                         throw new ObjectNotFoundException("No user specified");
2483                 long start = 0, end = 0;
2484                 if (logger.isDebugEnabled())
2485                         start = System.currentTimeMillis();
2486                 File result = new File(generateRepositoryFilePath());
2487                 try {
2488                         final FileOutputStream output = new FileOutputStream(result);
2489                         final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2490                         int n = 0;
2491
2492                         while (-1 != (n = stream.read(buffer)))
2493                                 output.write(buffer, 0, n);
2494                         output.close();
2495                         stream.close();
2496                 } catch (IOException e) {
2497                         if (!result.delete())
2498                                 logger.warn("Could not delete " + result.getPath());
2499                         throw e;
2500                 }
2501                 if (logger.isDebugEnabled()) {
2502                         end = System.currentTimeMillis();
2503                         logger.debug("Time to upload: " + (end - start) + " (msec)");
2504                 }
2505                 return result;
2506         }
2507
2508
2509         public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2510
2511                 if (userId == null)
2512                         throw new ObjectNotFoundException("No user specified");
2513                 User user = dao.getEntityById(User.class, userId);
2514                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2515                 if(status == null){
2516                         status = new FileUploadStatus();
2517                         status.setOwner(user);
2518                         status.setFilename(filename);
2519                         status.setBytesUploaded(bytesTransfered);
2520                         status.setFileSize(fileSize);
2521                         dao.create(status);
2522                 }
2523                 else{
2524                         status.setBytesUploaded(bytesTransfered);
2525                         status.setFileSize(fileSize);
2526                         dao.update(status);
2527                 }
2528
2529         }
2530
2531         public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2532                 if (userId == null)
2533                         throw new ObjectNotFoundException("No user specified");
2534                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2535                 if(status != null)
2536                         dao.delete(status);
2537         }
2538
2539         @Override
2540         public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2541                 return dao.getFileUploadStatus(userId, fileName);
2542         }
2543
2544         @Override
2545         public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2546                 if (userId == null)
2547                         throw new ObjectNotFoundException("No user specified");
2548                 if (folderId == null)
2549                         throw new ObjectNotFoundException("No folder specified");
2550                 final User user = dao.getEntityById(User.class, userId);
2551                 final Folder folder = dao.getEntityById(Folder.class, folderId);
2552                 // Check permissions
2553                 if (!folder.hasReadPermission(user))
2554                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2555                 List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
2556                 if (folder.hasReadPermission(user))
2557                         for (Folder f : folder.getSubfolders())
2558                                 if (f.hasReadPermission(user) && !f.isDeleted())
2559                                         subfolders.add(f.getDTO());
2560                 FolderDTO result = folder.getDTO();
2561                 result.setSubfolders(subfolders);
2562                 return folder.getDTO();
2563         }
2564
2565         @Override
2566         public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2567                 if (userId == null)
2568                         throw new ObjectNotFoundException("No user specified");
2569                 if (folderId == null)
2570                         throw new ObjectNotFoundException("No folder specified");
2571                 User user = dao.getEntityById(User.class, callingUserId);
2572                 Folder folder = dao.getEntityById(Folder.class, folderId);
2573                 // Check permissions
2574                 if (!folder.hasReadPermission(user))
2575                         throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2576
2577                 FolderDTO result = folder.getDTO();
2578                 result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2579                 return result;
2580         }
2581
2582         @Override
2583         public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
2584                         throws ObjectNotFoundException, InsufficientPermissionsException {
2585                 if (userId == null)
2586                         throw new ObjectNotFoundException("No user specified");
2587                 if (fileId == null)
2588                         throw new ObjectNotFoundException("No file specified");
2589                 if (version < 1)
2590                         throw new ObjectNotFoundException("No valid version specified");
2591                 User user = dao.getEntityById(User.class, userId);
2592                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2593                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2594                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2595                 FileBody body = dao.getFileVersion(fileId, version);
2596                 return body.getDTO();
2597         }
2598
2599         @Override
2600         public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2601                 if (userId == null)
2602                         throw new ObjectNotFoundException("No user specified");
2603                 User user = dao.getEntityById(User.class, userId);
2604                 user.setAcceptedPolicy(isAccepted);
2605                 return user;
2606         }
2607
2608         @Override
2609         public void updateAccounting(User user, Date date, long bandwidthDiff) {
2610                 dao.updateAccounting(user, date, bandwidthDiff);
2611         }
2612
2613         @Override
2614         public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2615                 if (userId == null)
2616                         throw new ObjectNotFoundException("No user specified");
2617                 if (folderId == null)
2618                         throw new ObjectNotFoundException("No folder specified");
2619                 User user = dao.getEntityById(User.class, userId);
2620                 Folder folder = dao.getEntityById(Folder.class, folderId);
2621                 // Check permissions
2622                 if (!folder.hasReadPermission(user))
2623                         return false;
2624                 return true;
2625         }
2626
2627         @Override
2628         public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2629                 if (userId == null)
2630                         throw new ObjectNotFoundException("No user specified");
2631                 User user = dao.getEntityById(User.class, userId);
2632                 user.generateWebDAVPassword();
2633                 return user.getWebDAVPassword();
2634         }
2635
2636         @Override
2637         public Invitation findInvite(String code) {
2638                 if (code == null)
2639                         return null;
2640                 return dao.findInvite(code);
2641         }
2642
2643         @Override
2644         public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2645                 LDAPConnection lc = new LDAPConnection();
2646         LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2647         attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2648         attributeSet.add(new LDAPAttribute("uid", username));
2649         attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2650         attributeSet.add(new LDAPAttribute("sn", lastname));
2651         attributeSet.add(new LDAPAttribute("givenName", firstname));
2652         attributeSet.add(new LDAPAttribute("mail", email));
2653         attributeSet.add(new LDAPAttribute("userPassword", password));
2654         String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2655         LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2656         try {
2657                 lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2658                 lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2659                                 getConfiguration().getString("bindPassword").getBytes("UTF8"));
2660                 lc.add(newEntry);
2661                 logger.info("Successfully added LDAP account: " + dn);
2662                 lc.disconnect();
2663         } catch(LDAPException e) {
2664                 throw new RuntimeException(e);
2665         } catch(UnsupportedEncodingException e) {
2666                 throw new RuntimeException(e);
2667         }
2668
2669         }
2670
2671 }