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