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