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