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