Added special message for AUTH users
[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, String homeOrg) 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         user.setHomeOrganization(homeOrg);
1421                 dao.create(user);
1422                 // Make sure we get an ID in the user object.
1423                 dao.flush();
1424                 // Create the root folder for the user.
1425                 createFolder(user.getName(), null, user);
1426                 return user;
1427         }
1428
1429         /**
1430          * Get the default user class, which is the one with the lowest quota.
1431          */
1432         private UserClass getDefaultUserClass() {
1433                 return getUserClasses().get(0);
1434         }
1435
1436         @Override
1437         public List<UserClass> getUserClasses() {
1438                 List<UserClass> classes = dao.getUserClasses();
1439                 // Create a default user class for first-time use. Afterwards, the
1440                 // admin should modify or add to the userclass table.
1441                 if (classes.size() == 0) {
1442                         UserClass defaultClass = new UserClass();
1443                         defaultClass.setName("default");
1444                         Long defaultQuota = getConfiguration().getLong("quota", new Long(52428800L));
1445                         defaultClass.setQuota(defaultQuota);
1446                         dao.create(defaultClass);
1447                         classes.add(defaultClass);
1448                 }
1449                 return classes;
1450         }
1451
1452         @Override
1453         public User findUserByEmail(String email) {
1454                 return dao.findUserByEmail(email);
1455         }
1456
1457         @Override
1458         public void updateUser(User user) {
1459                 dao.update(user);
1460         }
1461
1462         @Override
1463         public User findUser(String username) {
1464                 if (username == null)
1465                         return null;
1466                 return dao.findUser(username);
1467         }
1468
1469         @Override
1470         public User updateUserToken(Long userId) throws ObjectNotFoundException {
1471                 if (userId == null)
1472                         throw new ObjectNotFoundException("No user specified");
1473                 User user = dao.getEntityById(User.class, userId);
1474                 user.generateAuthToken();
1475                 return user;
1476         }
1477
1478         @Override
1479         public Set<Permission> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1480                 if (userId == null)
1481                         throw new ObjectNotFoundException("No user specified");
1482                 if (folderId == null)
1483                         throw new ObjectNotFoundException("No folder specified");
1484                 User user = dao.getEntityById(User.class, userId);
1485                 Folder folder = dao.getEntityById(Folder.class, folderId);
1486                 if(!folder.hasReadPermission(user))
1487                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1488                 Set<Permission> perms = folder.getPermissions();
1489                 Set<Permission> result = new LinkedHashSet<Permission>();
1490                 for (Permission perm : perms)
1491                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1492                                 result.add(perm);
1493                 for (Permission perm : perms)
1494                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1495                         } else
1496                                 result.add(perm);
1497                 return result;
1498
1499         }
1500
1501         /**
1502          * Set the provided permissions as the new permissions of the specified
1503          * folder.
1504          *
1505          * @param user
1506          * @param folder
1507          * @param permissions
1508          * @throws ObjectNotFoundException
1509          * @throws InsufficientPermissionsException
1510          */
1511         private void setFolderPermissions(User user, Folder folder, Set<Permission> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1512                 if (permissions != null && !permissions.isEmpty()) {
1513                         User owner = folder.getOwner();
1514                         Permission ownerPerm = null;
1515                         for (Permission perm : permissions)
1516                                 if (perm.getUser() != null && perm.getUser().getId().equals(owner.getId())) {
1517                                         ownerPerm = perm;
1518                                         break;
1519                                 }
1520                         if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1521                                 throw new InsufficientPermissionsException("Can't remove permissions from owner");
1522                         // Delete previous entries
1523                         for (Permission perm: folder.getPermissions())
1524                                 dao.delete(perm);
1525                         folder.getPermissions().clear();
1526                         for (Permission p : permissions) {
1527                                 // Skip 'empty' permission entries.
1528                                 if (!p.getRead() && !p.getWrite() && !p.getModifyACL()) continue;
1529                                 folder.addPermission(getPermission(p));
1530                         }
1531                         dao.update(folder);
1532                         for (FileHeader file : folder.getFiles()) {
1533                                 setFilePermissions(file, permissions);
1534                                 Date now = new Date();
1535                                 file.getAuditInfo().setModificationDate(now);
1536                                 file.getAuditInfo().setModifiedBy(user);
1537                         }
1538                         for (Folder sub : folder.getSubfolders())
1539                                 setFolderPermissions(user, sub, permissions);
1540                 }
1541         }
1542
1543         private Permission getPermission(Permission perm) throws ObjectNotFoundException {
1544                 Permission res = new Permission();
1545                 if (perm.getGroup() != null)
1546                         res.setGroup(dao.getEntityById(Group.class, perm.getGroup().getId()));
1547                 else if (perm.getUser() != null)
1548                         if (perm.getUser().getId() == null)
1549                                 res.setUser(dao.getUser(perm.getUser().getUsername()));
1550                         else
1551                                 res.setUser(dao.getEntityById(User.class, perm.getUser().getId()));
1552                 res.setRead(perm.hasRead());
1553                 res.setWrite(perm.hasWrite());
1554                 res.setModifyACL(perm.hasModifyACL());
1555                 return res;
1556         }
1557
1558         /* (non-Javadoc)
1559          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
1560          */
1561         @Override
1562         public List<User> getUsersByUserNameLike(String username) {
1563                 List<User> users = dao.getUsersByUserNameLike(username);
1564                 return users;
1565
1566         }
1567
1568         @Override
1569         public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1570                 if (userId == null)
1571                         throw new ObjectNotFoundException("No user specified");
1572                 if (groupId == null)
1573                         throw new ObjectNotFoundException("No group specified");
1574                 if (userToAddId == null)
1575                         throw new ObjectNotFoundException("No user to add specified");
1576                 User user = dao.getEntityById(User.class, userId);
1577                 Group group = dao.getEntityById(Group.class, groupId);
1578                 if (!group.getOwner().equals(user))
1579                         throw new InsufficientPermissionsException();
1580                 User userToAdd = dao.getEntityById(User.class, userToAddId);
1581                 if (group.contains(userToAdd))
1582                         throw new DuplicateNameException("User already exists in group");
1583                 group.getMembers().add(userToAdd);
1584                 dao.update(group);
1585
1586         }
1587
1588         @Override
1589         public void invalidateUserToken(Long userId) throws ObjectNotFoundException {
1590                 if (userId == null)
1591                         throw new ObjectNotFoundException("No user specified");
1592                 User user = dao.getEntityById(User.class, userId);
1593                 user.invalidateAuthToken();
1594                 return;
1595         }
1596
1597         @Override
1598         public List<Folder> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
1599                 if (userId == null)
1600                         throw new ObjectNotFoundException("No user specified");
1601                 List<Folder> folders = dao.getSharedRootFolders(userId);
1602                 List<Folder> result = new ArrayList<Folder>();
1603                 for (Folder f : folders) {
1604                         Folder lf = f;
1605                         lf.setSubfolders(getSharedSubfolders(userId, f.getId()));
1606                         result.add(lf);
1607                 }
1608                 return result;
1609         }
1610
1611         @Override
1612         public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
1613                 if (userId == null)
1614                         throw new ObjectNotFoundException("No user specified");
1615                 if (groupId == null)
1616                         throw new ObjectNotFoundException("No group specified");
1617                 if (memberId == null)
1618                         throw new ObjectNotFoundException("No member specified");
1619                 User owner = dao.getEntityById(User.class, userId);
1620                 Group group = dao.getEntityById(Group.class, groupId);
1621                 User member = dao.getEntityById(User.class, memberId);
1622                 if (!group.getOwner().equals(owner))
1623                         throw new InsufficientPermissionsException("User is not the owner of the group");
1624                 group.removeMemberFromGroup(member);
1625                 dao.update(group);
1626
1627         }
1628
1629         @Override
1630         public List<User> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
1631                 List<User> users = dao.getUsersSharingFoldersForUser(userId);
1632                 List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
1633                 List<User> result = new ArrayList<User>();
1634                 for (User u : users)
1635                         result.add(u);
1636                 for(User fu : usersFiles)
1637                         if(!users.contains(fu))
1638                                 result.add(fu);
1639                 return result;
1640         }
1641
1642         @Override
1643         public Set<Permission> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1644                 if (userId == null)
1645                         throw new ObjectNotFoundException("No user specified");
1646                 if (fileId == null)
1647                         throw new ObjectNotFoundException("No folder specified");
1648                 User user = dao.getEntityById(User.class, userId);
1649                 FileHeader folder = dao.getEntityById(FileHeader.class, fileId);
1650                 if(!folder.hasReadPermission(user))
1651                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1652                 Set<Permission> perms = folder.getPermissions();
1653                 Set<Permission> result = new LinkedHashSet<Permission>();
1654                 for (Permission perm : perms)
1655                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1656                                 result.add(perm);
1657                 for (Permission perm : perms)
1658                         if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1659                         } else
1660                                 result.add(perm);
1661                 return result;
1662         }
1663
1664         /**
1665          * Set the provided permissions as the new permissions of the specified
1666          * file. This method sets the modification date/user attributes to the
1667          * current values as a side effect.
1668          *
1669          * @param file
1670          * @param permissions
1671          * @throws ObjectNotFoundException
1672          * @throws InsufficientPermissionsException
1673          */
1674         private void setFilePermissions(FileHeader file,
1675                                 Set<Permission> permissions)
1676                         throws ObjectNotFoundException, InsufficientPermissionsException {
1677                 if (permissions != null && !permissions.isEmpty()) {
1678                         Permission ownerPerm = null;
1679                         for (Permission perm : permissions)
1680                                 if (perm.getUser() != null && perm.getUser().getId().equals(file.getOwner().getId())) {
1681                                         ownerPerm = perm;
1682                                         break;
1683                                 }
1684                         if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1685                                 throw new InsufficientPermissionsException("Can't remove permissions from owner");
1686                         // Delete previous entries.
1687                         for (Permission perm: file.getPermissions())
1688                                 dao.delete(perm);
1689                         file.getPermissions().clear();
1690                         for (Permission perm : permissions) {
1691                                 // Skip 'empty' permission entries.
1692                                 if (!perm.getRead() && !perm.getWrite() && !perm.getModifyACL()) continue;
1693                                 file.addPermission(getPermission(perm));
1694                         }
1695                         dao.flush();
1696                 }
1697         }
1698
1699         @Override
1700         public List<FileHeader> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1701                 if (userId == null)
1702                         throw new ObjectNotFoundException("No user specified");
1703                 List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1704                 return files;
1705         }
1706
1707         @Override
1708         public List<FileHeader> getSharedFiles(Long userId) throws ObjectNotFoundException {
1709                 if (userId == null)
1710                         throw new ObjectNotFoundException("No user specified");
1711                 List<FileHeader> files = dao.getSharedFiles(userId);
1712                 return files;
1713         }
1714
1715         @Override
1716         public List<Folder> getSharedFolders(Long userId) throws ObjectNotFoundException {
1717                 if (userId == null)
1718                         throw new ObjectNotFoundException("No user specified");
1719                 List<Folder> folders = dao.getSharedFolders(userId);
1720                 return folders;
1721         }
1722
1723         @Override
1724         public List<FileHeader> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1725                 if (ownerId == null)
1726                         throw new ObjectNotFoundException("No owner specified");
1727                 if (callingUserId == null)
1728                         throw new ObjectNotFoundException("No calling user specified");
1729                 List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1730                 return folders;
1731         }
1732
1733         @Override
1734         public List<Folder> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1735                 if (ownerId == null)
1736                         throw new ObjectNotFoundException("No owner specified");
1737                 if (callingUserId == null)
1738                         throw new ObjectNotFoundException("No calling user specified");
1739                 List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
1740                 List<Folder> result = new ArrayList<Folder>();
1741                 for (Folder f : folders) {
1742                         Folder lf = f;
1743                         lf.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
1744                         result.add(lf);
1745                 }
1746                 return result;
1747
1748         }
1749
1750         @Override
1751         public List<Folder> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1752                 if (userId == null)
1753                         throw new ObjectNotFoundException("No user specified");
1754                 if (folderId == null)
1755                         throw new ObjectNotFoundException("No folder specified");
1756                 User user = dao.getEntityById(User.class, userId);
1757                 Folder folder = dao.getEntityById(Folder.class, folderId);
1758                 List<Folder> result = new ArrayList<Folder>();
1759                 if (folder.isShared(user) || folder.isReadForAll())
1760                         for (Folder f : folder.getSubfolders())
1761                                 if ((f.isShared(user) || f.isReadForAll()) && !f.isDeleted())
1762                                         result.add(f);
1763                 return result;
1764         }
1765
1766         @Override
1767         public List<Folder> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
1768                 if (userId == null)
1769                         throw new ObjectNotFoundException("No user specified");
1770                 if (callingUserId == null)
1771                         throw new ObjectNotFoundException("No user specified");
1772                 if (folderId == null)
1773                         throw new ObjectNotFoundException("No folder specified");
1774                 User user = dao.getEntityById(User.class, callingUserId);
1775                 Folder folder = dao.getEntityById(Folder.class, folderId);
1776                 List<Folder> result = new ArrayList<Folder>();
1777                 if (folder.isSharedForOtherUser(user))
1778                         for (Folder f : folder.getSubfolders())
1779                                 if (f.isSharedForOtherUser(user) && !f.isDeleted()){
1780                                         Folder lf = f;
1781                                         lf.setSubfolders(getSharedSubfolders(userId, callingUserId, lf.getId()));
1782                                         result.add(lf);
1783                                 }
1784                 return result;
1785
1786         }
1787
1788         @Override
1789         public List<FileHeader> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1790         long startTime = System.currentTimeMillis();
1791                 if (userId == null)
1792                         throw new ObjectNotFoundException("No user specified");
1793                 User user = getUser(userId);
1794                 if (query == null)
1795                         throw new ObjectNotFoundException("No query specified");
1796                 List<FileHeader> files = search(user.getId(), query);
1797                 
1798         long stopTime = System.currentTimeMillis();
1799         logger.info("Total time: " + (stopTime - startTime));
1800                 return files;
1801         }
1802
1803         /**
1804          * Performs the actuals search on the solr server and returns the results
1805          *
1806          * @param userId
1807          * @param query
1808          * @return a List of FileHeader objects
1809          */
1810         private List<FileHeader> search(Long userId, String query) {
1811         final int maxRows = 100;
1812                 List<FileHeader> result = new ArrayList<FileHeader>();
1813                 try {
1814                         CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
1815             List<Group> groups = dao.getGroupsContainingUser(userId);
1816             String constructedQuery = escapeCharacters(normalizeSearchQuery(query)) + " AND (public: true OR ureaders: " + userId;
1817             if (!groups.isEmpty()) {
1818                 constructedQuery += " OR (";
1819                 for (int i=0; i<groups.size(); i++) {
1820                     Group g = groups.get(i);
1821                     constructedQuery += "greaders :" + g.getId();
1822                     if (i < groups.size() - 1)
1823                         constructedQuery += " OR ";
1824                 }
1825                 constructedQuery += ")";
1826             }
1827             constructedQuery += ")";
1828                         SolrQuery solrQuery = new SolrQuery(constructedQuery);
1829             solrQuery.setRows(maxRows);
1830             long startTime = System.currentTimeMillis();
1831                         QueryResponse response = solr.query(solrQuery);
1832                         SolrDocumentList results = response.getResults();
1833             if (results.getNumFound() > maxRows) {
1834                 solrQuery.setRows(Integer.valueOf((int) results.getNumFound()));
1835                 response = solr.query(solrQuery);
1836                 results = response.getResults();
1837             }
1838             long stopTime = System.currentTimeMillis();
1839             logger.info("Search time:" +  (stopTime - startTime));
1840                         User user = getUser(userId);
1841             startTime = System.currentTimeMillis();
1842                         for (SolrDocument d : results) {
1843                                 Long id = Long.valueOf((String) d.getFieldValue("id"));
1844                                 try {
1845                                         FileHeader f = dao.getEntityById(FileHeader.class, id);
1846                                         result.add(f);
1847                                 } catch (ObjectNotFoundException e) {
1848                                         logger.warn("Search result id " + id + " cannot be found", e);
1849                                 }
1850                         }
1851             stopTime = System.currentTimeMillis();
1852             logger.info("File loads: " + (stopTime - startTime));
1853                 } catch (MalformedURLException e) {
1854                         logger.error(e);
1855                         throw new EJBException(e);
1856                 } catch (SolrServerException e) {
1857                         logger.error(e);
1858                         throw new EJBException(e);
1859                 } catch (ObjectNotFoundException e) {
1860                         logger.error(e);
1861                         throw new EJBException(e);
1862                 }
1863                 return result;
1864         }
1865
1866         @Override
1867         public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1868                 for(Long l : fileIds){
1869                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1870                         copyFile(userId, l, destId, file.getName());
1871                 }
1872
1873
1874         }
1875
1876         @Override
1877         public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1878                 for(Long l : fileIds){
1879                         FileHeader file = dao.getEntityById(FileHeader.class, l);
1880                         moveFile(userId, l, destId, file.getName());
1881                 }
1882
1883         }
1884
1885         @Override
1886         public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1887                 if (userId == null)
1888                         throw new ObjectNotFoundException("No user specified");
1889                 User user = dao.getEntityById(User.class, userId);
1890                 Nonce nonce = Nonce.createNonce(user.getId());
1891                 dao.create(nonce);
1892                 return nonce;
1893         }
1894
1895         @Override
1896         public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1897                 if (userId == null)
1898                         throw new ObjectNotFoundException("No user specified");
1899                 if (nonce == null)
1900                         throw new ObjectNotFoundException("No nonce specified");
1901                 return dao.getNonce(nonce, userId);
1902         }
1903
1904         @Override
1905         public void removeNonce(Long id) throws ObjectNotFoundException {
1906                 if (id == null)
1907                         throw new ObjectNotFoundException("No nonce specified");
1908                 Nonce nonce = dao.getEntityById(Nonce.class, id);
1909                 dao.delete(nonce);
1910         }
1911
1912         @Override
1913         public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1914                 if (userId == null)
1915                         throw new ObjectNotFoundException("No user specified");
1916                 User user = dao.getEntityById(User.class, userId);
1917                 user.setNonce(nonce);
1918                 user.setNonceExpiryDate(nonceExpiryDate);
1919         }
1920
1921         @Override
1922         public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
1923                 if (userId == null)
1924                         throw new ObjectNotFoundException("No user specified");
1925                 StatsDTO stats = new StatsDTO();
1926                 stats.setFileCount(dao.getFileCount(userId));
1927                 Long fileSize = dao.getFileSize(userId);
1928                 stats.setFileSize(fileSize);
1929                 Long quota = getQuota(userId);
1930                 Long quotaLeft = quota - fileSize;
1931                 stats.setQuotaLeftSize(quotaLeft);
1932                 return stats;
1933         }
1934
1935         @Override
1936         public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
1937                 if (userId == null)
1938                         throw new ObjectNotFoundException("No user specified");
1939                 if (fileId == null)
1940                         throw new ObjectNotFoundException("No file specified");
1941                 User user = dao.getEntityById(User.class, userId);
1942                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1943                 if(!header.hasWritePermission(user))
1944                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1945                 FileBody body = dao.getFileVersion(fileId, version);
1946                 final File fileContents = new File(body.getStoredFilePath());
1947
1948                 try {
1949                         updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
1950                 } catch (FileNotFoundException e) {
1951                         throw new GSSIOException(e);
1952                 }
1953
1954         }
1955
1956         /* (non-Javadoc)
1957          * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
1958          */
1959         @Override
1960         public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1961                 if (userId == null)
1962                         throw new ObjectNotFoundException("No user specified");
1963                 if (fileId == null)
1964                         throw new ObjectNotFoundException("No file specified");
1965                 User user = dao.getEntityById(User.class, userId);
1966                 FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1967                 if(!header.hasWritePermission(user))
1968                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
1969                 Iterator<FileBody> it = header.getBodies().iterator();
1970                 while(it.hasNext()){
1971                         FileBody body = it.next();
1972                         if(!body.equals(header.getCurrentBody())){
1973                                 deleteActualFile(body.getStoredFilePath());
1974                                 it.remove();
1975                                 dao.delete(body);
1976                         }
1977                 }
1978                 header.getCurrentBody().setVersion(1);
1979
1980                 Folder parent = header.getFolder();
1981                 touchParentFolders(parent, user, new Date());
1982         }
1983
1984         /**
1985          * Gets the quota left for specified user ID.
1986          */
1987         private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
1988                 Long fileSize = dao.getFileSize(userId);
1989                 Long quota = getQuota(userId);
1990                 return quota - fileSize;
1991         }
1992
1993         /**
1994          * Gets the quota for specified user ID.
1995          */
1996         private Long getQuota(Long userId) throws ObjectNotFoundException{
1997                 UserClass uc = getUser(userId).getUserClass();
1998                 if (uc == null)
1999                         uc = getDefaultUserClass();
2000                 return uc.getQuota();
2001         }
2002
2003         @Override
2004     @TransactionAttribute(TransactionAttributeType.NEVER)
2005         public String rebuildSolrIndex() {
2006                 try {
2007             CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2008                         solr.deleteByQuery("*:*");
2009                         solr.commit();
2010             logger.info("Deleted everything in solr");
2011
2012                         List<Long> fileIds = dao.getAllFileIds();
2013             logger.info("Total of " + fileIds.size() + " will be indexed");
2014             int i = 0;
2015                         for (Long id : fileIds) {
2016                 try {
2017                     postFileToSolr(solr, id);
2018                 }
2019                 catch (ObjectNotFoundException e) {
2020                     logger.error("Indexing of file id " + id + " failed.", e);
2021                 }
2022                 i++;
2023                 if (i % 10 == 0) {
2024                     solr.commit();
2025                     logger.info("Sent commit to solr at file " + i);
2026                 }
2027                         }
2028                         solr.optimize();
2029                         solr.commit();
2030             logger.info("Finished indexing of " + i + " files");
2031             return "Finished indexing of " + i + " files";
2032                 } catch (IOException e) {
2033                         throw new EJBException(e);
2034                 } catch (SolrServerException e) {
2035                         throw new EJBException(e);
2036                 }
2037         }
2038
2039         @Override
2040     @TransactionAttribute(TransactionAttributeType.NEVER)
2041         public String refreshSolrIndex() {
2042                 try {
2043                         CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2044                         
2045                         List<Long> fileIds = dao.getAllFileIds();
2046             logger.info("Total of " + fileIds.size() + " will be checked");
2047             int i = 0;
2048                         for (Long id : fileIds) {
2049                 if (!fileIsInSolr(solr, id)) {
2050                     try {
2051                         postFileToSolr(solr, id);
2052                     }
2053                     catch (ObjectNotFoundException e) {
2054                         logger.error("Indexing of file id " + id + " failed.", e);
2055                     }
2056                 }
2057                 i++;
2058                 if (i % 10 == 0) {
2059                     solr.commit();
2060                     logger.info("Sent commit to solr at file " + i);
2061                 }
2062                         }
2063                         solr.optimize();
2064                         solr.commit();
2065             logger.info("Finished indexing of " + i + " files");
2066             return "Finished indexing of " + i + " files";
2067                 } catch (IOException e) {
2068                         throw new EJBException(e);
2069                 } catch (SolrServerException e) {
2070                         throw new EJBException(e);
2071                 }
2072         }
2073
2074     private boolean fileIsInSolr(CommonsHttpSolrServer solr, Long id) {
2075         try {
2076             SolrQuery query = new SolrQuery("id:" + id);
2077             QueryResponse response = solr.query(query);
2078             return !(response.getResults().size() == 0);
2079         }
2080         catch (SolrServerException e) {
2081             logger.warn("Exception while checking file " + id, e);
2082             return false;
2083         }
2084     }
2085
2086     @Override
2087         public FileHeader createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2088                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2089                         InsufficientPermissionsException, QuotaExceededException {
2090                 // Validate.
2091                 if (userId == null)
2092                         throw new ObjectNotFoundException("No user specified");
2093                 if (folderId == null)
2094                         throw new ObjectNotFoundException("No folder specified");
2095                 String contentType = mimeType;
2096                 if (StringUtils.isEmpty(mimeType))
2097                         contentType = DEFAULT_MIME_TYPE;
2098                 if (StringUtils.isEmpty(name))
2099                         throw new ObjectNotFoundException("No file name specified");
2100                 if (dao.existsFolderOrFile(folderId, name))
2101                         throw new DuplicateNameException("A folder or file with the name '" + name +
2102                                                 "' already exists at this level");
2103
2104                 // Do the actual work.
2105                 Folder parent = null;
2106                 try {
2107                         parent = dao.getEntityById(Folder.class, folderId);
2108                 } catch (final ObjectNotFoundException onfe) {
2109                         // Supply a more accurate problem description.
2110                         throw new ObjectNotFoundException("Parent folder not found");
2111                 }
2112                 final User owner = dao.getEntityById(User.class, userId);
2113                 if (!parent.hasWritePermission(owner))
2114                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2115                 final FileHeader file = new FileHeader();
2116                 file.setName(name);
2117                 parent.addFile(file);
2118                 // set file owner to folder owner
2119                 file.setOwner(parent.getOwner());
2120                 //set file's readForAll value according to parent folder readForAll value
2121                 file.setReadForAll(parent.isReadForAll());
2122
2123                 final Date now = new Date();
2124                 final AuditInfo auditInfo = new AuditInfo();
2125                 auditInfo.setCreatedBy(owner);
2126                 auditInfo.setCreationDate(now);
2127                 auditInfo.setModifiedBy(owner);
2128                 auditInfo.setModificationDate(now);
2129                 file.setAuditInfo(auditInfo);
2130                 // TODO set the proper versioning flag on creation
2131                 file.setVersioned(false);
2132
2133                 for (final Permission p : parent.getPermissions()) {
2134                         final Permission permission = new Permission();
2135                         permission.setGroup(p.getGroup());
2136                         permission.setUser(p.getUser());
2137                         permission.setRead(p.getRead());
2138                         permission.setWrite(p.getWrite());
2139                         permission.setModifyACL(p.getModifyACL());
2140                         file.addPermission(permission);
2141                 }
2142
2143                 // Create the file body.
2144                 try {
2145                         createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2146                 } catch (FileNotFoundException e) {
2147                         throw new GSSIOException(e);
2148                 }
2149                 touchParentFolders(parent, owner, new Date());
2150                 dao.flush();
2151                 indexFile(file.getId(), false);
2152
2153                 return file;
2154         }
2155
2156         @Override
2157         public FileHeader updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2158                 if (userId == null)
2159                         throw new ObjectNotFoundException("No user specified");
2160                 if (fileId == null)
2161                         throw new ObjectNotFoundException("No file specified");
2162                 String contentType = mimeType;
2163
2164                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2165
2166                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2167                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2168                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2169                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2170                         contentType = identifyMimeType(file.getName());
2171
2172                 final User owner = dao.getEntityById(User.class, userId);
2173                 if (!file.hasWritePermission(owner))
2174                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2175                 final Date now = new Date();
2176                 final AuditInfo auditInfo = new AuditInfo();
2177                 auditInfo.setCreatedBy(owner);
2178                 auditInfo.setCreationDate(now);
2179                 auditInfo.setModifiedBy(owner);
2180                 auditInfo.setModificationDate(now);
2181                 try {
2182                         createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2183                 } catch (FileNotFoundException e) {
2184                         throw new GSSIOException(e);
2185                 }
2186                 Folder parent = file.getFolder();
2187                 touchParentFolders(parent, owner, new Date());
2188
2189                 indexFile(fileId, false);
2190                 return file;
2191         }
2192
2193         /**
2194          * Helper method for identifying mime type by examining the filename extension
2195          *
2196          * @param filename
2197          * @return the mime type
2198          */
2199         private String identifyMimeType(String filename) {
2200                 if (filename.indexOf('.') != -1) {
2201                         String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2202                         if (".doc".equals(extension))
2203                                 return "application/msword";
2204                         else if (".xls".equals(extension))
2205                                 return "application/vnd.ms-excel";
2206                         else if (".ppt".equals(extension))
2207                                 return "application/vnd.ms-powerpoint";
2208                         else if (".pdf".equals(extension))
2209                                 return "application/pdf";
2210                         else if (".gif".equals(extension))
2211                                 return "image/gif";
2212                         else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2213                                 return "image/jpeg";
2214                         else if (".tiff".equals(extension) || ".tif".equals(extension))
2215                                 return "image/tiff";
2216                         else if (".png".equals(extension))
2217                                 return "image/png";
2218                         else if (".bmp".equals(extension))
2219                                 return "image/bmp";
2220                 }
2221                 // when all else fails assign the default mime type
2222                 return DEFAULT_MIME_TYPE;
2223         }
2224
2225         /**
2226          * Helper method to create a new file body and attach it as the current body
2227          * of the provided file header.
2228          *
2229          * @param name the original file name
2230          * @param mimeType the content type
2231          * @param fileSize the uploaded file size
2232          * @param filePath the uploaded file full path
2233          * @param header the file header that will be associated with the new body
2234          * @param auditInfo the audit info
2235          * @throws FileNotFoundException
2236          * @throws QuotaExceededException
2237          * @throws ObjectNotFoundException if the owner was not found
2238          */
2239         private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2240                                 FileHeader header, AuditInfo auditInfo)
2241                         throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2242
2243                 long currentTotalSize = 0;
2244                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2245                         currentTotalSize = header.getTotalSize();
2246                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2247                 if(quotaLeft < fileSize-currentTotalSize) {
2248                         // quota exceeded -> delete the file
2249                         deleteActualFile(filePath);
2250                         throw new QuotaExceededException("Not enough free space available");
2251                 }
2252
2253                 FileBody body = new FileBody();
2254
2255                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2256                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2257                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2258                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2259                         body.setMimeType(identifyMimeType(name));
2260                 else
2261                         body.setMimeType(mimeType);
2262                 body.setAuditInfo(auditInfo);
2263                 body.setFileSize(fileSize);
2264                 body.setOriginalFilename(name);
2265                 body.setStoredFilePath(filePath);
2266                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2267                 if(!header.isVersioned() && header.getCurrentBody() != null){
2268                         header.setCurrentBody(null);
2269                         if (header.getBodies() != null) {
2270                                 Iterator<FileBody> it = header.getBodies().iterator();
2271                                 while(it.hasNext()){
2272                                         FileBody bo = it.next();
2273                                         deleteActualFile(bo.getStoredFilePath());
2274                                         it.remove();
2275                                         dao.delete(bo);
2276                                 }
2277                         }
2278                 }
2279
2280                 dao.flush();
2281                 header.addBody(body);
2282                 header.setAuditInfo(auditInfo);
2283
2284                 dao.create(body);
2285         }
2286
2287
2288         @Override
2289         @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2290         public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2291                 if (userId == null)
2292                         throw new ObjectNotFoundException("No user specified");
2293                 User owner = dao.getEntityById(User.class, userId);
2294                 if(owner == null)
2295                         throw new ObjectNotFoundException("No user specified");
2296                 long start = 0, end = 0;
2297                 if (logger.isDebugEnabled())
2298                         start = System.currentTimeMillis();
2299                 File result = new File(generateRepositoryFilePath());
2300                 try {
2301                         final FileOutputStream output = new FileOutputStream(result);
2302                         final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2303                         int n = 0;
2304
2305                         while (-1 != (n = stream.read(buffer)))
2306                                 output.write(buffer, 0, n);
2307                         output.close();
2308                         stream.close();
2309                 } catch (IOException e) {
2310                         if (!result.delete())
2311                                 logger.warn("Could not delete " + result.getPath());
2312                         throw e;
2313                 }
2314                 if (logger.isDebugEnabled()) {
2315                         end = System.currentTimeMillis();
2316                         logger.debug("Time to upload: " + (end - start) + " (msec)");
2317                 }
2318                 return result;
2319         }
2320
2321
2322         @Override
2323         public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2324
2325                 if (userId == null)
2326                         throw new ObjectNotFoundException("No user specified");
2327                 User user = dao.getEntityById(User.class, userId);
2328                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2329                 if(status == null){
2330                         status = new FileUploadStatus();
2331                         status.setOwner(user);
2332                         status.setFilename(filename);
2333                         status.setBytesUploaded(bytesTransfered);
2334                         status.setFileSize(fileSize);
2335                         dao.create(status);
2336                 }
2337                 else{
2338                         status.setBytesUploaded(bytesTransfered);
2339                         status.setFileSize(fileSize);
2340                         dao.update(status);
2341                 }
2342
2343         }
2344
2345         @Override
2346         public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2347                 if (userId == null)
2348                         throw new ObjectNotFoundException("No user specified");
2349                 FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2350                 if(status != null)
2351                         dao.delete(status);
2352         }
2353
2354         @Override
2355         public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2356                 return dao.getFileUploadStatus(userId, fileName);
2357         }
2358
2359         @Override
2360         public FileBody getFileVersion(Long userId, Long fileId, int version)
2361                         throws ObjectNotFoundException, InsufficientPermissionsException {
2362                 if (userId == null)
2363                         throw new ObjectNotFoundException("No user specified");
2364                 if (fileId == null)
2365                         throw new ObjectNotFoundException("No file specified");
2366                 if (version < 1)
2367                         throw new ObjectNotFoundException("No valid version specified");
2368                 User user = dao.getEntityById(User.class, userId);
2369                 FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2370                 if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2371                         throw new InsufficientPermissionsException("You don't have the necessary permissions");
2372                 FileBody body = dao.getFileVersion(fileId, version);
2373                 return body;
2374         }
2375
2376         @Override
2377         public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2378                 if (userId == null)
2379                         throw new ObjectNotFoundException("No user specified");
2380                 User user = dao.getEntityById(User.class, userId);
2381                 user.setAcceptedPolicy(isAccepted);
2382                 return user;
2383         }
2384
2385         @Override
2386         public void updateAccounting(User user, Date date, long bandwidthDiff) {
2387                 dao.updateAccounting(user, date, bandwidthDiff);
2388         }
2389
2390         @Override
2391         public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2392                 if (userId == null)
2393                         throw new ObjectNotFoundException("No user specified");
2394                 if (folderId == null)
2395                         throw new ObjectNotFoundException("No folder specified");
2396                 User user = dao.getEntityById(User.class, userId);
2397                 Folder folder = dao.getEntityById(Folder.class, folderId);
2398                 // Check permissions
2399                 if (!folder.hasReadPermission(user))
2400                         return false;
2401                 return true;
2402         }
2403
2404         @Override
2405         public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2406                 if (userId == null)
2407                         throw new ObjectNotFoundException("No user specified");
2408                 User user = dao.getEntityById(User.class, userId);
2409                 user.generateWebDAVPassword();
2410                 return user.getWebDAVPassword();
2411         }
2412
2413         @Override
2414         public Invitation findInvite(String code) {
2415                 if (code == null)
2416                         return null;
2417                 return dao.findInvite(code);
2418         }
2419
2420         @Override
2421         public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2422                 LDAPConnection lc = new LDAPConnection();
2423         LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2424         attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2425         attributeSet.add(new LDAPAttribute("uid", username));
2426         attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2427         attributeSet.add(new LDAPAttribute("sn", lastname));
2428         attributeSet.add(new LDAPAttribute("givenName", firstname));
2429         attributeSet.add(new LDAPAttribute("mail", email));
2430         attributeSet.add(new LDAPAttribute("userPassword", password));
2431         String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2432         LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2433         try {
2434                 lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2435                 lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2436                                 getConfiguration().getString("bindPassword").getBytes("UTF8"));
2437                 lc.add(newEntry);
2438                 logger.info("Successfully added LDAP account: " + dn);
2439                 lc.disconnect();
2440         } catch(LDAPException e) {
2441                 throw new RuntimeException(e);
2442         } catch(UnsupportedEncodingException e) {
2443                 throw new RuntimeException(e);
2444         }
2445
2446         }
2447
2448         @Override
2449         public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
2450                 User user = findUser(username);
2451                 if (user == null)
2452                         throw new ObjectNotFoundException("The user was not found");
2453                 Invitation invite = findInvite(code);
2454                 if (invite.getUser() != null)
2455                         throw new InvitationUsedException("This code has already been used");
2456                 invite.setUser(user);
2457                 UserClass couponClass = getCouponUserClass();
2458                 user.setUserClass(couponClass);
2459                 return couponClass;
2460         }
2461
2462         @Override
2463         public UserClass getCouponUserClass() {
2464                 return dao.findCouponUserClass();
2465         }
2466
2467         /**
2468          * Set the provided readForAll as the new readforAll value of the specified
2469          * folder and sub-folders.
2470          *
2471          * @param user
2472          * @param folder
2473          * @param readForAll
2474          * @throws ObjectNotFoundException
2475          *
2476          */
2477         private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
2478                 if (readForAll != null && user.equals(folder.getOwner())){
2479                         folder.setReadForAll(readForAll);
2480                         dao.update(folder);
2481                         for (FileHeader file : folder.getFiles())
2482                                 file.setReadForAll(readForAll);
2483                         if(readForAll)
2484                                 //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
2485                                 for (Folder sub : folder.getSubfolders())
2486                                         setFolderReadForAll(user, sub, readForAll);
2487
2488                 }
2489
2490         }
2491                 
2492         /**
2493          * Update the userLogin with the values from the supplied object.
2494          */
2495         
2496         public void addUserLogin(UserLogin userLogin) {
2497                 dao.update(userLogin);          
2498
2499         }
2500                 
2501         /**
2502          * Retrieves the current session user login and the user's last login
2503          * 
2504          * @param userId
2505          * @return a list of last two user logins
2506          * @throws ObjectNotFoundException 
2507          */
2508         
2509         public List<UserLogin> getLastUserLogins(Long userId) throws ObjectNotFoundException{
2510                 List<UserLogin> userLoginResults = new ArrayList<UserLogin>();          
2511                 userLoginResults = dao.getLoginsForUser(userId);        
2512                 if(userLoginResults.size() == 0)
2513                         throw new ObjectNotFoundException("No userlogin found for the user");
2514                 //if the user logins for the first time lastLoginDate = currentLoginDate
2515                 if(userLoginResults.size()==1)
2516                         userLoginResults.add(userLoginResults.get(0));
2517                 return userLoginResults;
2518         }
2519         
2520
2521     public void postFileToSolr(Long id) throws IOException, SolrServerException, ObjectNotFoundException {
2522         CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2523         postFileToSolr(solr, id);
2524         solr.commit();
2525     }
2526
2527         private void postFileToSolr(CommonsHttpSolrServer solr, Long id) throws ObjectNotFoundException {
2528                 try {
2529                         FileHeader file = dao.getFileForIndexing(id);
2530                         FileBody body = file.getCurrentBody();
2531                         String mime = body.getMimeType();
2532                         boolean multipart = true;
2533                         if (!mime.equals("application/pdf") 
2534                                                 && !mime.equals("text/plain")
2535                                                 && !mime.equals("text/html")
2536                                                 && !mime.endsWith("msword")
2537                                                 && !mime.endsWith("ms-excel")
2538                                                 && !mime.endsWith("powerpoint")
2539                                                 || (body.getFileSize() > getConfiguration().getLong("solrDocumentUploadLimitInKB") * 1024))
2540                                 multipart = false;
2541
2542                         if (!multipart)
2543                                 sendMetaDataOnly(solr, file);
2544                         else {
2545                 ContentStreamUpdateRequest solrRequest = new ContentStreamUpdateRequest(getConfiguration().getString("solr.rich.update.path"));
2546                                 solrRequest.setParam("literal.id", file.getId().toString());
2547                                 solrRequest.setParam("literal.name", file.getName());
2548                                 for (FileTag t : file.getFileTags()) {
2549                                         solrRequest.getParams().add("literal.tag", t.getTag());
2550                                 }
2551                 for (Permission p : file.getPermissions()) {
2552                     if (p.getRead()) {
2553                         if (p.getUser() != null)
2554                             solrRequest.getParams().add("literal.ureaders", p.getUser().getId().toString());
2555                         else if (p.getGroup() != null)
2556                             solrRequest.getParams().add("literal.greaders", p.getGroup().getId().toString());
2557                     }
2558                 }
2559                 solrRequest.setParam("literal.owner", file.getOwner().getId().toString());
2560                 solrRequest.setParam("literal.public", String.valueOf(file.isReadForAll()));
2561                 File fsFile = new File(body.getStoredFilePath());
2562                                 solrRequest.addFile(fsFile);
2563                                 try {
2564                                         solr.request(solrRequest);
2565                                 }
2566                                 catch (SolrException e) {
2567                                         logger.warn("File " + id + " failed with SolrException: " + e.getLocalizedMessage() + ". Retrying without the file");
2568                                         //Let 's try without the file
2569                                         sendMetaDataOnly(solr, file);
2570                                 }
2571                                 catch (NullPointerException e) {
2572                                         logger.warn("File " + id + " failed with NullPointerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2573                                         //Let 's try without the file
2574                                         sendMetaDataOnly(solr, file);
2575                                 }
2576                                 catch (SolrServerException e) {
2577                                         logger.warn("File " + id + " failed with SolrServerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2578                                         //Let 's try without the file
2579                                         sendMetaDataOnly(solr, file);
2580                                 }
2581                         }
2582                 } catch (MalformedURLException e) {
2583                         throw new EJBException(e);
2584                 } catch (SolrServerException e) {
2585                         throw new EJBException(e);
2586                 } catch (IOException e) {
2587                         throw new EJBException(e);
2588                 }
2589         }
2590
2591         private void sendMetaDataOnly(CommonsHttpSolrServer solr, FileHeader file) throws SolrServerException, IOException {
2592                 SolrInputDocument solrDoc = new SolrInputDocument();
2593                 solrDoc.addField("id", file.getId().toString());
2594                 solrDoc.addField("name", file.getName());
2595                 for (FileTag t : file.getFileTags()) {
2596                         solrDoc.addField("tag", t.getTag());
2597                 }
2598         for (Permission p : file.getPermissions()) {
2599             if (p.getRead()) {
2600                 if (p.getUser() != null)
2601                     solrDoc.addField("ureaders", p.getUser().getId());
2602                 else if (p.getGroup() != null)
2603                     solrDoc.addField("greaders", p.getGroup().getId());
2604             }
2605         }
2606         solrDoc.addField("owner", file.getOwner().getId());
2607         solrDoc.addField("public", file.isReadForAll());
2608                 solr.add(solrDoc);
2609         }
2610
2611         private String tokenizeFilename(String filename){
2612                 StringBuffer result = new StringBuffer();
2613                 StringTokenizer tokenizer = new StringTokenizer(filename,"._");
2614                 while(tokenizer.hasMoreTokens()){
2615                         result.append(tokenizer.nextToken());
2616                         result.append(" ");
2617                 }
2618                 result.append(filename);
2619                 return result.toString();
2620         }
2621
2622         private String normalizeSearchQuery(String query) {
2623                 if (query.contains("*"))
2624                         return query.toLowerCase().replace('ά', 'α').replace('έ', 'ε').replace('ί', 'ι').replace('ή', 'η').replace('ύ', 'υ')
2625                                         .replace('ό', 'ο').replace('ς', 'σ').replace('ώ', 'ω').replace('ϊ', 'ι').replace('ϋ', 'υ');
2626                 else
2627                         return query;
2628         }
2629         
2630         private String escapeCharacters(String text) {
2631                 return text.replaceAll(":", "\\\\:");
2632         }
2633         
2634         /*** NEW METHODS IN ORDER TO AVOID LAZY loading exception in json render 
2635          ****/
2636         @Override
2637         public Folder expandFolder(Folder folder) throws ObjectNotFoundException{
2638                 Folder result = dao.getEntityById(Folder.class, folder.getId());
2639                 result.getSubfolders().size();
2640                 result.getFiles().size();
2641                 result.getPermissions().size();
2642                 return result;
2643 }
2644
2645         @Override
2646         public FileHeader expandFile(FileHeader folder) throws ObjectNotFoundException{
2647                 FileHeader result = dao.getEntityById(FileHeader.class, folder.getId());
2648                 result.getFolder();
2649                 result.getPermissions().size();
2650                 result.getFileTags().size();
2651                 return result;
2652         }
2653         
2654         @Override
2655         public Group expandGroup(Group folder) throws ObjectNotFoundException{
2656                 Group result = dao.getEntityById(Group.class, folder.getId());
2657                 result.getMembers().size();
2658                 return result;
2659         }
2660
2661         /* (non-Javadoc)
2662          * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
2663          */
2664         @Override
2665         public User getUserByUserName(String username) {
2666                 User result = dao.getUserByUserName(username);
2667                 return result;
2668         }
2669         
2670         /*WEBDAV CREATE EMPTY FILE*/
2671         @Override
2672         public FileHeader createEmptyFile(Long userId, Long folderId, String name)
2673                         throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2674                         InsufficientPermissionsException, QuotaExceededException {
2675                 // Validate.
2676                 if (userId == null)
2677                         throw new ObjectNotFoundException("No user specified");
2678                 if (folderId == null)
2679                         throw new ObjectNotFoundException("No folder specified");
2680                 String contentType = DEFAULT_MIME_TYPE;
2681                 if (StringUtils.isEmpty(name))
2682                         throw new ObjectNotFoundException("No file name specified");
2683                 if (dao.existsFolderOrFile(folderId, name))
2684                         throw new DuplicateNameException("A folder or file with the name '" + name +
2685                                                 "' already exists at this level");
2686
2687                 // Do the actual work.
2688                 Folder parent = null;
2689                 try {
2690                         parent = dao.getEntityById(Folder.class, folderId);
2691                 } catch (final ObjectNotFoundException onfe) {
2692                         // Supply a more accurate problem description.
2693                         throw new ObjectNotFoundException("Parent folder not found");
2694                 }
2695                 final User owner = dao.getEntityById(User.class, userId);
2696                 if (!parent.hasWritePermission(owner))
2697                         throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2698                 final FileHeader file = new FileHeader();
2699                 file.setName(name);
2700                 parent.addFile(file);
2701                 // set file owner to folder owner
2702                 file.setOwner(parent.getOwner());
2703                 //set file's readForAll value according to parent folder readForAll value
2704                 file.setReadForAll(parent.isReadForAll());
2705
2706                 final Date now = new Date();
2707                 final AuditInfo auditInfo = new AuditInfo();
2708                 auditInfo.setCreatedBy(owner);
2709                 auditInfo.setCreationDate(now);
2710                 auditInfo.setModifiedBy(owner);
2711                 auditInfo.setModificationDate(now);
2712                 file.setAuditInfo(auditInfo);
2713                 // TODO set the proper versioning flag on creation
2714                 file.setVersioned(false);
2715
2716                 for (final Permission p : parent.getPermissions()) {
2717                         final Permission permission = new Permission();
2718                         permission.setGroup(p.getGroup());
2719                         permission.setUser(p.getUser());
2720                         permission.setRead(p.getRead());
2721                         permission.setWrite(p.getWrite());
2722                         permission.setModifyACL(p.getModifyACL());
2723                         file.addPermission(permission);
2724                 }
2725                 // Create the file body.
2726                 try {
2727                         createEmptyFileBody(name, contentType, 0,  file, auditInfo);
2728                 } catch (FileNotFoundException e) {
2729                         throw new GSSIOException(e);
2730                 }
2731                 touchParentFolders(parent, owner, new Date());
2732                 dao.flush();
2733                 return file;
2734         }
2735         
2736         private void createEmptyFileBody(String name, String mimeType, long fileSize, 
2737                                 FileHeader header, AuditInfo auditInfo)
2738                         throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2739
2740                 long currentTotalSize = 0;
2741                 if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2742                         currentTotalSize = header.getTotalSize();
2743                 Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2744                 
2745
2746                 FileBody body = new FileBody();
2747
2748                 // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2749                 if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2750                                         || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2751                                         || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2752                         body.setMimeType(identifyMimeType(name));
2753                 else
2754                         body.setMimeType(mimeType);
2755                 body.setAuditInfo(auditInfo);
2756                 body.setFileSize(fileSize);
2757                 body.setOriginalFilename(name);
2758                 body.setStoredFilePath(generateRepositoryFilePath());
2759                 //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2760                 if(!header.isVersioned() && header.getCurrentBody() != null){
2761                         header.setCurrentBody(null);
2762                         if (header.getBodies() != null) {
2763                                 Iterator<FileBody> it = header.getBodies().iterator();
2764                                 while(it.hasNext()){
2765                                         FileBody bo = it.next();
2766                                         deleteActualFile(bo.getStoredFilePath());
2767                                         it.remove();
2768                                         dao.delete(bo);
2769                                 }
2770                         }
2771                 }
2772
2773                 dao.flush();
2774                 header.addBody(body);
2775                 header.setAuditInfo(auditInfo);
2776
2777                 dao.create(body);
2778         }
2779         /*** WEBDAV LOCK **/
2780         @Override
2781         public FileLock getLockById(String id) {
2782                 return dao.getLockById(id);
2783         }
2784
2785         @Override
2786         public FileLock getLockByToken(String tokenId) {
2787                 return dao.getLockByToken(tokenId);
2788         }
2789
2790         @Override
2791         public void removeLock(FileLock lock) {
2792                 dao.removeLock(lock);           
2793         }
2794
2795         @Override
2796         public FileLock saveOrUpdateLock(FileLock lock) {
2797                 return dao.saveOrUpdateLock(lock);
2798         }
2799         
2800         @Override
2801         public WebDavNonce getWebDavNonce(String tokenId) {
2802                 return dao.getWebDavNonce(tokenId);
2803         }
2804
2805         @Override
2806         public void removeWebDavNonce(WebDavNonce nonce) {
2807                 dao.removeWebDavNonce(nonce);           
2808         }
2809
2810         @Override
2811         public WebDavNonce saveOrUpdateWebDavNonce(WebDavNonce nonce) {
2812                 return dao.saveOrUpdateWebDavNonce(nonce);
2813         }
2814 }